soc.lore.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
@ 2023-04-24  7:30 Huisong Li
  2023-04-24  8:09 ` Arnd Bergmann
                   ` (8 more replies)
  0 siblings, 9 replies; 79+ messages in thread
From: Huisong Li @ 2023-04-24  7:30 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo, arnd
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	huangdaode, lihuisong

The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
for ensuring cache coherent on HiSilicon SoC. The performance of
the application may be affected if some hccs ports are in non-full
lane status, have a large number of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   18 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1296 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  204 ++++
 7 files changed, 1528 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eddbc48c61e9..fe0e796e8445 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9399,6 +9399,12 @@ S:	Maintained
 W:	http://www.hisilicon.com
 F:	drivers/spi/spi-hisi-sfc-v3xx.c
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HMM - Heterogeneous Memory Management
 M:	Jérôme Glisse <jglisse@redhat.com>
 L:	linux-mm@kvack.org
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 3b0f9fb3b5c8..531f46f3ad98 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..81768d47f572
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ARM64 && ACPI
+	help
+	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
+	  for ensuring cache coherent on HiSilicon SoC. The performance of
+	  the application may be affected if some hccs ports are in non-full
+	  lane status, have a large number of CRC errors and so on.
+
+	  Say M here if you want to include support for querying the health
+	  status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..2b52f7dedc78
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1296 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
+ * ensuring cache coherent on HiSilicon SoC.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve info as belows each port:
+ *    - port type
+ *    - lane mode
+ *    - using status
+ *    - current lane mode
+ *    - link state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve info as belows all ports on die or chip:
+ *    - if all used ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+static bool hccs_dev_property_supported(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+
+	if (hdev->intr_mode) {
+		dev_err(dev, "interrupt mode is unsupported.\n");
+		return false;
+	}
+
+	if (hdev->type >= ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE) {
+		dev_err(dev, "PCC type-%u is unsupported.\n", hdev->type);
+		return false;
+	}
+
+	return true;
+}
+
+static int hccs_get_device_property(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+
+	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
+		dev_err(hdev->dev, "no device-flags property.\n");
+		return -ENODEV;
+	}
+
+	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
+		dev_err(hdev->dev, "no pcc-type property.\n");
+		return -ENODEV;
+	}
+
+	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
+		dev_err(hdev->dev, "no pcc-channel property.\n");
+		return -ENODEV;
+	}
+
+	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
+	if (!hccs_dev_property_supported(hdev))
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (hdev->intr_mode)
+		return;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	if (hdev->intr_mode)
+		return 0;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_relaxed_poll_timeout(&comm_base->status, status,
+					 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+					 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+					 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->is_used = attrs[i].is_used;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sprintf(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr =
+			__ATTR(type, 0444, type_show, NULL);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sprintf(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr =
+			__ATTR(lane_mode, 0444, lane_mode_show, NULL);
+
+static ssize_t using_status_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sprintf(buf, "%u\n", port->is_used);
+}
+
+static struct kobj_attribute using_status_attr =
+			__ATTR(using_status, 0444, using_status_show, NULL);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get current lane num failed\n");
+
+	return sprintf(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr =
+			__ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get link fsm failed\n");
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sprintf(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr =
+			__ATTR(link_fsm, 0444, link_fsm_show, NULL);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get lane mask failed\n");
+
+	return sprintf(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr =
+			__ATTR(lane_mask, 0444, lane_mask_show, NULL);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get crc error count failed\n");
+
+	return sprintf(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr =
+			__ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&using_status_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get link status failed\n");
+
+	return sprintf(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get full lane status failed\n");
+
+	return sprintf(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return sprintf(buf, "get total crc error count failed\n");
+
+	return sprintf(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return sprintf(buf, "get all linked status failed\n");
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sprintf(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return sprintf(buf, "get full lane status failed\n");
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sprintf(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return sprintf(buf, "get full lane status failed\n");
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sprintf(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	pr_info("kunpeng_hccs is initializing.\n");
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	rc = hccs_get_device_property(hdev);
+	if (rc)
+		return rc;
+
+	mutex_init(&hdev->lock);
+
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto err_uninit_mbox_client;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto err_uninit_mbox_client;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto err_uninit_mbox_client;
+
+	dev_info(&pdev->dev, "HISI HCCS driver registered!\n");
+
+	return 0;
+
+err_uninit_mbox_client:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1", },
+};
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = ACPI_PTR(hccs_acpi_match),
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..ca557ef115ea
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool is_used; /* if the link is used. */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+enum HCCS_DEV_FLAG_BITS {
+	/* 1 means that driver works on the interrupt mode. */
+	HCCS_DEV_FLAGS_INTR_B = 0,
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u32 flags;
+	u8 type;
+	bool intr_mode;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u32 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 is_used : 1; /* if the link is used. */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-04-24  8:09 ` Arnd Bergmann
  2023-04-25  3:04   ` lihuisong (C)
  2023-04-25 10:30   ` Sudeep Holla
  2023-04-24  8:42 ` Krzysztof Kozlowski
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 79+ messages in thread
From: Arnd Bergmann @ 2023-04-24  8:09 UTC (permalink / raw)
  To: Huisong Li, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	huangdaode, linux-acpi, Len Brown, Rafael J. Wysocki, devicetree,
	Rob Herring, Frank Rowand, Krzysztof Kozlowski

On Mon, Apr 24, 2023, at 09:30, Huisong Li wrote:

> diff --git a/drivers/soc/hisilicon/Kconfig 
> b/drivers/soc/hisilicon/Kconfig
> new file mode 100644
> index 000000000000..81768d47f572
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Kconfig
> @@ -0,0 +1,18 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Hisilicon SoC drivers"
> +	depends on ARCH_HISI
> +
> +config KUNPENG_HCCS
> +	tristate "HCCS driver on Kunpeng SoC"
> +	depends on ARM64 && ACPI

Is there a compile-time dependency on ARM64? If not, it would
be good to allow compile testing. At the same time, you
can probably tighten this to ARCH_HISI instead of ARM64,
since no other vendors are going to use it:

       depends on ACPI
       depends on (ARM64 && ARCH_HISI) || COMPILE_TEST

> +
> +#include "kunpeng_hccs.h"
> +
> +/* PCC defines */
> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)

Should these perhaps be in include/acpi/pcc.h? The 0x50434300
number is just "PCC\0", so it appears to not be HCCS specific.

> +
> +static int hccs_get_device_property(struct hccs_dev *hdev)
> +{
> +	struct device *dev = hdev->dev;
> +
> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
> +		dev_err(hdev->dev, "no device-flags property.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
> +		dev_err(hdev->dev, "no pcc-type property.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
> +		dev_err(hdev->dev, "no pcc-channel property.\n");
> +		return -ENODEV;
> +	}
> +
> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
> +	if (!hccs_dev_property_supported(hdev))
> +		return -EOPNOTSUPP;
> +

Where are the device properties documented? I'm never quite sure how
to handle these for ACPI-only drivers, since we don't normally have the
bindings in Documentation/devicetree/bindings/, but it feels like there
should be some properly reviewed document somewhere else.

Adding ACPI and devicetree maintainers to Cc for clarification. 

> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
> +	u16 status;
> +	int ret;
> +
> +	/*
> +	 * Poll PCC status register every 3us(delay_us) for maximum of
> +	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
> +	 */
> +	ret = readw_relaxed_poll_timeout(&comm_base->status, status,
> +					 status & HCCS_PCC_STATUS_CMD_COMPLETE,
> +					 HCCS_POLL_STATUS_TIME_INTERVAL_US,
> +					 cl_info->deadline_us);

Is it both safe and faster to use a relaxed readw here, compared
to the normal one? If there is any access to shared memory
involved, you need the implied barrier for serialization, and since this
is already a sleeping operation, I would guess that you don't care
about the last nanosecond of latency here.

> +static ssize_t hccs_show(struct kobject *k, struct attribute *attr, 
> char *buf)
> +{
> +	struct kobj_attribute *kobj_attr;
> +
> +	kobj_attr = container_of(attr, struct kobj_attribute, attr);
> +
> +	return kobj_attr->show(k, kobj_attr, buf);
> +}
> +
> +static const struct sysfs_ops hccs_comm_ops = {
> +	.show = hccs_show,
> +};

Every sysfs interface needs to be documented in Documentation/ABI/

> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h 
> b/drivers/soc/hisilicon/kunpeng_hccs.h
> new file mode 100644
> index 000000000000..ca557ef115ea
> --- /dev/null
> +++ b/drivers/soc/hisilicon/kunpeng_hccs.h
> @@ -0,0 +1,204 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/* Copyright (c) 2023 Hisilicon Limited. */
> +
> +#ifndef __KUNPENG_HCCS_H__
> +#define __KUNPENG_HCCS_H__

Are you planning to add more drivers that share this file? If not,
just fold the contents into the driver itself.

     Arnd

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-04-24  8:09 ` Arnd Bergmann
@ 2023-04-24  8:42 ` Krzysztof Kozlowski
  2023-04-25  3:16   ` lihuisong (C)
  2023-05-22  7:22 ` [PATCH v2 0/2] " Huisong Li
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 79+ messages in thread
From: Krzysztof Kozlowski @ 2023-04-24  8:42 UTC (permalink / raw)
  To: Huisong Li, andersson, matthias.bgg, angelogioacchino.delregno,
	shawnguo, arnd
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, huangdaode

On 24/04/2023 09:30, Huisong Li wrote:
> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> for ensuring cache coherent on HiSilicon SoC. The performance of
> the application may be affected if some hccs ports are in non-full
> lane status, have a large number of CRC errors and so on.
> 
> This driver provides the query interface of the health status and
> port information of HCCS on Kunpeng SoC.
> 
> Signed-off-by: Huisong Li <lihuisong@huawei.com>
> ---
>  MAINTAINERS                          |    6 +
>  drivers/soc/Kconfig                  |    1 +
>  drivers/soc/Makefile                 |    1 +
>  drivers/soc/hisilicon/Kconfig        |   18 +
>  drivers/soc/hisilicon/Makefile       |    2 +
>  drivers/soc/hisilicon/kunpeng_hccs.c | 1296 ++++++++++++++++++++++++++
>  drivers/soc/hisilicon/kunpeng_hccs.h |  204 ++++
>  7 files changed, 1528 insertions(+)
>  create mode 100644 drivers/soc/hisilicon/Kconfig
>  create mode 100644 drivers/soc/hisilicon/Makefile
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index eddbc48c61e9..fe0e796e8445 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9399,6 +9399,12 @@ S:	Maintained
>  W:	http://www.hisilicon.com
>  F:	drivers/spi/spi-hisi-sfc-v3xx.c
>  
> +HISILICON KUNPENG SOC HCCS DRIVER
> +M:	Huisong Li <lihuisong@huawei.com>
> +S:	Maintained
> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
> +
>  HMM - Heterogeneous Memory Management
>  M:	Jérôme Glisse <jglisse@redhat.com>
>  L:	linux-mm@kvack.org
> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
> index 4e176280113a..d21e75d69294 100644
> --- a/drivers/soc/Kconfig
> +++ b/drivers/soc/Kconfig
> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>  source "drivers/soc/canaan/Kconfig"
>  source "drivers/soc/fsl/Kconfig"
>  source "drivers/soc/fujitsu/Kconfig"
> +source "drivers/soc/hisilicon/Kconfig"
>  source "drivers/soc/imx/Kconfig"
>  source "drivers/soc/ixp4xx/Kconfig"
>  source "drivers/soc/litex/Kconfig"
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 3b0f9fb3b5c8..531f46f3ad98 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>  obj-y				+= fsl/
>  obj-y				+= fujitsu/
>  obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
> +obj-y				+= hisilicon/
>  obj-y				+= imx/
>  obj-y				+= ixp4xx/
>  obj-$(CONFIG_SOC_XWAY)		+= lantiq/
> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
> new file mode 100644
> index 000000000000..81768d47f572
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Kconfig
> @@ -0,0 +1,18 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Hisilicon SoC drivers"
> +	depends on ARCH_HISI
> +
> +config KUNPENG_HCCS
> +	tristate "HCCS driver on Kunpeng SoC"
> +	depends on ARM64 && ACPI
> +	help
> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
> +	  the application may be affected if some hccs ports are in non-full
> +	  lane status, have a large number of CRC errors and so on.
> +
> +	  Say M here if you want to include support for querying the health
> +	  status and port information of HCCS on Kunpeng SoC.
> +
> +endmenu
> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
> new file mode 100644
> index 000000000000..226e747e70d6
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
> new file mode 100644
> index 000000000000..2b52f7dedc78
> --- /dev/null
> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
> @@ -0,0 +1,1296 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
> + * ensuring cache coherent on HiSilicon SoC.
> + *
> + * Copyright (c) 2023 Hisilicon Limited.
> + * Author: Huisong Li <lihuisong@huawei.com>
> + *
> + * HCCS driver for Kunpeng SoC provides the following features:
> + * - Retrieve info as belows each port:
> + *    - port type
> + *    - lane mode
> + *    - using status
> + *    - current lane mode
> + *    - link state machine
> + *    - lane mask
> + *    - CRC error count
> + *
> + * - Retrieve info as belows all ports on die or chip:
> + *    - if all used ports are in linked
> + *    - if all linked ports are in full lane
> + *    - CRC error count sum
> + */
> +#include <linux/sysfs.h>
> +#include <linux/acpi.h>
> +#include <linux/io.h>
> +#include <linux/kobject.h>
> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>
> +#include <acpi/pcc.h>
> +
> +#include "kunpeng_hccs.h"
> +
> +/* PCC defines */
> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
> +
> +/*
> + * Arbitrary retries in case the remote processor is slow to respond
> + * to PCC commands
> + */
> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
> +
> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_port_info, kobj);
> +}
> +
> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_die_info, kobj);
> +}
> +
> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_chip_info, kobj);
> +}
> +
> +static bool hccs_dev_property_supported(struct hccs_dev *hdev)
> +{
> +	struct device *dev = hdev->dev;
> +
> +	if (hdev->intr_mode) {
> +		dev_err(dev, "interrupt mode is unsupported.\n");
> +		return false;
> +	}
> +
> +	if (hdev->type >= ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE) {
> +		dev_err(dev, "PCC type-%u is unsupported.\n", hdev->type);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static int hccs_get_device_property(struct hccs_dev *hdev)
> +{
> +	struct device *dev = hdev->dev;
> +
> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
> +		dev_err(hdev->dev, "no device-flags property.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
> +		dev_err(hdev->dev, "no pcc-type property.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
> +		dev_err(hdev->dev, "no pcc-channel property.\n");
> +		return -ENODEV;
> +	}
> +
> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
> +	if (!hccs_dev_property_supported(hdev))

Maybe leave a comment that all these are ACPI-only and are not allowed
in DT usage. Without bindings we are not going to review them. OTOH, I
think we don't have a process for such cases in general.

> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +

(...)

> +static struct attribute *hccs_chip_default_attrs[] = {
> +	&all_linked_on_chip_attr.attr,
> +	&linked_full_lane_on_chip_attr.attr,
> +	&crc_err_cnt_sum_on_chip_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(hccs_chip_default);
> +
> +static const struct kobj_type hccs_chip_type = {
> +	.sysfs_ops = &hccs_comm_ops,
> +	.default_groups = hccs_chip_default_groups,

Missing sysfs documentation.

This also looks like:
1. Duplicating parts of socinfo.
2. Open-coding coding sysfs API.

Anyway, after describing this API it might turn out it is not valid for
upstream usage...

> +};
> +
> +static void hccs_remove_die_dir(struct hccs_die_info *die)
> +{
> +	struct hccs_port_info *port;
> +	u8 i;
> +
> +	for (i = 0; i < die->port_num; i++) {
> +		port = &die->ports[i];
> +		if (port->dir_created)
> +			kobject_put(&port->kobj);
> +	}
> +
> +	kobject_put(&die->kobj);
> +}
> +
> +static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
> +{
> +	struct hccs_die_info *die;
> +	u8 i;
> +
> +	for (i = 0; i < chip->die_num; i++) {
> +		die = &chip->dies[i];
> +		if (die->dir_created)
> +			hccs_remove_die_dir(die);
> +	}
> +
> +	kobject_put(&chip->kobj);
> +}
> +
> +static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
> +{
> +	u8 i;
> +
> +	for (i = 0; i < hdev->chip_num; i++)
> +		hccs_remove_chip_dir(&hdev->chips[i]);
> +}
> +
> +static int hccs_create_hccs_dir(struct hccs_dev *hdev,
> +				struct hccs_die_info *die,
> +				struct hccs_port_info *port)
> +{
> +	int ret;
> +
> +	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
> +				   &die->kobj, "hccs%d", port->port_id);
> +	if (ret) {
> +		kobject_put(&port->kobj);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_create_die_dir(struct hccs_dev *hdev,
> +			       struct hccs_chip_info *chip,
> +			       struct hccs_die_info *die)
> +{
> +	struct hccs_port_info *port;
> +	int ret;
> +	u16 i;
> +
> +	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
> +				   &chip->kobj, "die%d", die->die_id);
> +	if (ret) {
> +		kobject_put(&die->kobj);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < die->port_num; i++) {
> +		port = &die->ports[i];
> +		ret = hccs_create_hccs_dir(hdev, die, port);
> +		if (ret) {
> +			dev_err(hdev->dev, "create hccs%d dir failed.\n",
> +				port->port_id);
> +			goto err;
> +		}
> +		port->dir_created = true;
> +	}
> +
> +	return 0;
> +err:
> +	hccs_remove_die_dir(die);
> +
> +	return ret;
> +}
> +
> +static int hccs_create_chip_dir(struct hccs_dev *hdev,
> +				struct hccs_chip_info *chip)
> +{
> +	struct hccs_die_info *die;
> +	int ret;
> +	u16 id;
> +
> +	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
> +				   &hdev->dev->kobj, "chip%d", chip->chip_id);
> +	if (ret) {
> +		kobject_put(&chip->kobj);
> +		return ret;
> +	}
> +
> +	for (id = 0; id < chip->die_num; id++) {
> +		die = &chip->dies[id];
> +		ret = hccs_create_die_dir(hdev, chip, die);
> +		if (ret)
> +			goto err;
> +		die->dir_created = true;
> +	}
> +
> +	return 0;
> +err:
> +	hccs_remove_chip_dir(chip);
> +
> +	return ret;
> +}
> +
> +static int hccs_create_topo_dirs(struct hccs_dev *hdev)
> +{
> +	struct hccs_chip_info *chip;
> +	u8 id, k;
> +	int ret;
> +
> +	for (id = 0; id < hdev->chip_num; id++) {
> +		chip = &hdev->chips[id];
> +		ret = hccs_create_chip_dir(hdev, chip);
> +		if (ret) {
> +			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +err:
> +	for (k = 0; k < id; k++)
> +		hccs_remove_chip_dir(&hdev->chips[k]);
> +
> +	return ret;
> +}
> +
> +static int hccs_probe(struct platform_device *pdev)
> +{
> +	struct acpi_device *acpi_dev;
> +	struct hccs_dev *hdev;
> +	int rc;
> +
> +	pr_info("kunpeng_hccs is initializing.\n");

Drop.

> +
> +	if (acpi_disabled) {
> +		dev_err(&pdev->dev, "acpi is disabled.\n");

Is it possible for your ACPI-only driver?


> +		return -ENODEV;
> +	}
> +	acpi_dev = ACPI_COMPANION(&pdev->dev);
> +	if (!acpi_dev)
> +		return -ENODEV;
> +
> +	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
> +	if (!hdev)
> +		return -ENOMEM;
> +	hdev->acpi_dev = acpi_dev;
> +	hdev->dev = &pdev->dev;
> +	platform_set_drvdata(pdev, hdev);
> +
> +	rc = hccs_get_device_property(hdev);
> +	if (rc)
> +		return rc;
> +
> +	mutex_init(&hdev->lock);
> +
> +	rc = hccs_register_pcc_channel(hdev);
> +	if (rc)
> +		return rc;
> +
> +	rc = hccs_get_dev_caps(hdev);
> +	if (rc)
> +		goto err_uninit_mbox_client;
> +
> +	rc = hccs_get_hw_info(hdev);
> +	if (rc)
> +		goto err_uninit_mbox_client;
> +
> +	rc = hccs_create_topo_dirs(hdev);
> +	if (rc)
> +		goto err_uninit_mbox_client;
> +
> +	dev_info(&pdev->dev, "HISI HCCS driver registered!\n");

Drop simple probe success msgs. There's already kernel infrastructure
giving you this information.

> +
> +	return 0;
> +
> +err_uninit_mbox_client:
> +	hccs_unregister_pcc_channel(hdev);
> +
> +	return rc;
> +}
> +
> +static int hccs_remove(struct platform_device *pdev)
> +{
> +	struct hccs_dev *hdev = platform_get_drvdata(pdev);
> +
> +	hccs_remove_topo_dirs(hdev);
> +	hccs_unregister_pcc_channel(hdev);
> +
> +	return 0;
> +}
> +
> +static const struct acpi_device_id hccs_acpi_match[] = {
> +	{ "HISI04B1", },
> +};
> +
> +static struct platform_driver hccs_driver = {
> +	.probe = hccs_probe,
> +	.remove = hccs_remove,
> +	.driver = {
> +		.name = "kunpeng_hccs",
> +		.acpi_match_table = ACPI_PTR(hccs_acpi_match),

Drop ACPI_PTR as it is not balanced with __maybe_unused.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  8:09 ` Arnd Bergmann
@ 2023-04-25  3:04   ` lihuisong (C)
  2023-04-25  6:08     ` Arnd Bergmann
  2023-04-25 10:30   ` Sudeep Holla
  1 sibling, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-04-25  3:04 UTC (permalink / raw)
  To: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	huangdaode, linux-acpi, Len Brown, Rafael J. Wysocki, devicetree,
	Rob Herring, Frank Rowand, Krzysztof Kozlowski

Hi Arnd,

Thanks for your review. My reply is as follows.


在 2023/4/24 16:09, Arnd Bergmann 写道:
> On Mon, Apr 24, 2023, at 09:30, Huisong Li wrote:
>
>> diff --git a/drivers/soc/hisilicon/Kconfig
>> b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..81768d47f572
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,18 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI
>> +
>> +config KUNPENG_HCCS
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ARM64 && ACPI
> Is there a compile-time dependency on ARM64? If not, it would
Yes, no compile-time dependency on ARM64.
> be good to allow compile testing. At the same time, you
> can probably tighten this to ARCH_HISI instead of ARM64,
> since no other vendors are going to use it:
>
>         depends on ACPI
>         depends on (ARM64 && ARCH_HISI) || COMPILE_TEST
What do you think of adjusting it as below?
menu "Hisilicon SoC drivers"
     depends on ARCH_HISI || COMPILE_TEST

config KUNPENG_HCCS
     depends on ACPI
     depends on ARM64 || COMPILE_TEST

>
>> +
>> +#include "kunpeng_hccs.h"
>> +
>> +/* PCC defines */
>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
> Should these perhaps be in include/acpi/pcc.h? The 0x50434300
> number is just "PCC\0", so it appears to not be HCCS specific.
This is a PCC signature. As stated in the APCI,
"The signature of a subspace is computed by a bitwiseor of the value 
0x50434300
with the subspace ID. For example, subspace 3 has the signature 0x50434303."

I am not sure if all driver need to use this fixed signature mask.
As far as I know, cppc_acpi.c didn't use this signature and 
xgene-hwmon.c used another mask defined in its driver.
So I place it here.
>
>> +
>> +static int hccs_get_device_property(struct hccs_dev *hdev)
>> +{
>> +	struct device *dev = hdev->dev;
>> +
>> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
>> +		dev_err(hdev->dev, "no device-flags property.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
>> +		dev_err(hdev->dev, "no pcc-type property.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
>> +		dev_err(hdev->dev, "no pcc-channel property.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
>> +	if (!hccs_dev_property_supported(hdev))
>> +		return -EOPNOTSUPP;
>> +
> Where are the device properties documented? I'm never quite sure how
> to handle these for ACPI-only drivers, since we don't normally have the
> bindings in Documentation/devicetree/bindings/, but it feels like there
> should be some properly reviewed document somewhere else.
These are ACPI-only, instead of DT.
I will add a comment here as Krzysztof suggested.
>
> Adding ACPI and devicetree maintainers to Cc for clarification.
>
>> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
>> +	u16 status;
>> +	int ret;
>> +
>> +	/*
>> +	 * Poll PCC status register every 3us(delay_us) for maximum of
>> +	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
>> +	 */
>> +	ret = readw_relaxed_poll_timeout(&comm_base->status, status,
>> +					 status & HCCS_PCC_STATUS_CMD_COMPLETE,
>> +					 HCCS_POLL_STATUS_TIME_INTERVAL_US,
>> +					 cl_info->deadline_us);
> Is it both safe and faster to use a relaxed readw here, compared
> to the normal one? If there is any access to shared memory
> involved, you need the implied barrier for serialization, and since this
> is already a sleeping operation, I would guess that you don't care
> about the last nanosecond of latency here.
Great comment. I will use the normal one.
>
>> +static ssize_t hccs_show(struct kobject *k, struct attribute *attr,
>> char *buf)
>> +{
>> +	struct kobj_attribute *kobj_attr;
>> +
>> +	kobj_attr = container_of(attr, struct kobj_attribute, attr);
>> +
>> +	return kobj_attr->show(k, kobj_attr, buf);
>> +}
>> +
>> +static const struct sysfs_ops hccs_comm_ops = {
>> +	.show = hccs_show,
>> +};
> Every sysfs interface needs to be documented in Documentation/ABI/
All right, I will add another patch to do this.
>
>> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h
>> b/drivers/soc/hisilicon/kunpeng_hccs.h
>> new file mode 100644
>> index 000000000000..ca557ef115ea
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.h
>> @@ -0,0 +1,204 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/* Copyright (c) 2023 Hisilicon Limited. */
>> +
>> +#ifndef __KUNPENG_HCCS_H__
>> +#define __KUNPENG_HCCS_H__
> Are you planning to add more drivers that share this file? If not,
> just fold the contents into the driver itself.
Yes, we will add more drivers in this file.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  8:42 ` Krzysztof Kozlowski
@ 2023-04-25  3:16   ` lihuisong (C)
  2023-04-25 11:24     ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-04-25  3:16 UTC (permalink / raw)
  To: Krzysztof Kozlowski, andersson, matthias.bgg,
	angelogioacchino.delregno, shawnguo, arnd
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, huangdaode

Hi Krzysztof,
Thanks for your review. My reply is as follows.

在 2023/4/24 16:42, Krzysztof Kozlowski 写道:
> On 24/04/2023 09:30, Huisong Li wrote:
>> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> for ensuring cache coherent on HiSilicon SoC. The performance of
>> the application may be affected if some hccs ports are in non-full
>> lane status, have a large number of CRC errors and so on.
>>
>> This driver provides the query interface of the health status and
>> port information of HCCS on Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>> ---
>>   MAINTAINERS                          |    6 +
>>   drivers/soc/Kconfig                  |    1 +
>>   drivers/soc/Makefile                 |    1 +
>>   drivers/soc/hisilicon/Kconfig        |   18 +
>>   drivers/soc/hisilicon/Makefile       |    2 +
>>   drivers/soc/hisilicon/kunpeng_hccs.c | 1296 ++++++++++++++++++++++++++
>>   drivers/soc/hisilicon/kunpeng_hccs.h |  204 ++++
>>   7 files changed, 1528 insertions(+)
>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index eddbc48c61e9..fe0e796e8445 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9399,6 +9399,12 @@ S:	Maintained
>>   W:	http://www.hisilicon.com
>>   F:	drivers/spi/spi-hisi-sfc-v3xx.c
>>   
>> +HISILICON KUNPENG SOC HCCS DRIVER
>> +M:	Huisong Li <lihuisong@huawei.com>
>> +S:	Maintained
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
>> +
>>   HMM - Heterogeneous Memory Management
>>   M:	Jérôme Glisse <jglisse@redhat.com>
>>   L:	linux-mm@kvack.org
>> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
>> index 4e176280113a..d21e75d69294 100644
>> --- a/drivers/soc/Kconfig
>> +++ b/drivers/soc/Kconfig
>> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>>   source "drivers/soc/canaan/Kconfig"
>>   source "drivers/soc/fsl/Kconfig"
>>   source "drivers/soc/fujitsu/Kconfig"
>> +source "drivers/soc/hisilicon/Kconfig"
>>   source "drivers/soc/imx/Kconfig"
>>   source "drivers/soc/ixp4xx/Kconfig"
>>   source "drivers/soc/litex/Kconfig"
>> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
>> index 3b0f9fb3b5c8..531f46f3ad98 100644
>> --- a/drivers/soc/Makefile
>> +++ b/drivers/soc/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>>   obj-y				+= fsl/
>>   obj-y				+= fujitsu/
>>   obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
>> +obj-y				+= hisilicon/
>>   obj-y				+= imx/
>>   obj-y				+= ixp4xx/
>>   obj-$(CONFIG_SOC_XWAY)		+= lantiq/
>> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..81768d47f572
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,18 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI
>> +
>> +config KUNPENG_HCCS
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ARM64 && ACPI
>> +	help
>> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
>> +	  the application may be affected if some hccs ports are in non-full
>> +	  lane status, have a large number of CRC errors and so on.
>> +
>> +	  Say M here if you want to include support for querying the health
>> +	  status and port information of HCCS on Kunpeng SoC.
>> +
>> +endmenu
>> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
>> new file mode 100644
>> index 000000000000..226e747e70d6
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Makefile
>> @@ -0,0 +1,2 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
>> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
>> new file mode 100644
>> index 000000000000..2b52f7dedc78
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
>> @@ -0,0 +1,1296 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
>> + * ensuring cache coherent on HiSilicon SoC.
>> + *
>> + * Copyright (c) 2023 Hisilicon Limited.
>> + * Author: Huisong Li <lihuisong@huawei.com>
>> + *
>> + * HCCS driver for Kunpeng SoC provides the following features:
>> + * - Retrieve info as belows each port:
>> + *    - port type
>> + *    - lane mode
>> + *    - using status
>> + *    - current lane mode
>> + *    - link state machine
>> + *    - lane mask
>> + *    - CRC error count
>> + *
>> + * - Retrieve info as belows all ports on die or chip:
>> + *    - if all used ports are in linked
>> + *    - if all linked ports are in full lane
>> + *    - CRC error count sum
>> + */
>> +#include <linux/sysfs.h>
>> +#include <linux/acpi.h>
>> +#include <linux/io.h>
>> +#include <linux/kobject.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/platform_device.h>
>> +#include <acpi/pcc.h>
>> +
>> +#include "kunpeng_hccs.h"
>> +
>> +/* PCC defines */
>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
>> +
>> +/*
>> + * Arbitrary retries in case the remote processor is slow to respond
>> + * to PCC commands
>> + */
>> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
>> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
>> +
>> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_port_info, kobj);
>> +}
>> +
>> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_die_info, kobj);
>> +}
>> +
>> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_chip_info, kobj);
>> +}
>> +
>> +static bool hccs_dev_property_supported(struct hccs_dev *hdev)
>> +{
>> +	struct device *dev = hdev->dev;
>> +
>> +	if (hdev->intr_mode) {
>> +		dev_err(dev, "interrupt mode is unsupported.\n");
>> +		return false;
>> +	}
>> +
>> +	if (hdev->type >= ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE) {
>> +		dev_err(dev, "PCC type-%u is unsupported.\n", hdev->type);
>> +		return false;
>> +	}
>> +
>> +	return true;
>> +}
>> +
>> +static int hccs_get_device_property(struct hccs_dev *hdev)
>> +{
>> +	struct device *dev = hdev->dev;
>> +
>> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
>> +		dev_err(hdev->dev, "no device-flags property.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
>> +		dev_err(hdev->dev, "no pcc-type property.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
>> +		dev_err(hdev->dev, "no pcc-channel property.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
>> +	if (!hccs_dev_property_supported(hdev))
> Maybe leave a comment that all these are ACPI-only and are not allowed
> in DT usage. Without bindings we are not going to review them. OTOH, I
> think we don't have a process for such cases in general.
Thanks for your suggestion. will add it.
>
>> +		return -EOPNOTSUPP;
>> +
>> +	return 0;
>> +}
>> +
> (...)
>
>> +static struct attribute *hccs_chip_default_attrs[] = {
>> +	&all_linked_on_chip_attr.attr,
>> +	&linked_full_lane_on_chip_attr.attr,
>> +	&crc_err_cnt_sum_on_chip_attr.attr,
>> +	NULL,
>> +};
>> +ATTRIBUTE_GROUPS(hccs_chip_default);
>> +
>> +static const struct kobj_type hccs_chip_type = {
>> +	.sysfs_ops = &hccs_comm_ops,
>> +	.default_groups = hccs_chip_default_groups,
> Missing sysfs documentation.
I will add another patch to do this.
>
> This also looks like:
> 1. Duplicating parts of socinfo.
Where is the duplicating part. Sorry, I cannot get it.
> 2. Open-coding coding sysfs API.
I cannot understand it. Can you be more specific?
>
> Anyway, after describing this API it might turn out it is not valid for
> upstream usage...
>
>> +};
>> +
>> +static void hccs_remove_die_dir(struct hccs_die_info *die)
>> +{
>> +	struct hccs_port_info *port;
>> +	u8 i;
>> +
>> +	for (i = 0; i < die->port_num; i++) {
>> +		port = &die->ports[i];
>> +		if (port->dir_created)
>> +			kobject_put(&port->kobj);
>> +	}
>> +
>> +	kobject_put(&die->kobj);
>> +}
>> +
>> +static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
>> +{
>> +	struct hccs_die_info *die;
>> +	u8 i;
>> +
>> +	for (i = 0; i < chip->die_num; i++) {
>> +		die = &chip->dies[i];
>> +		if (die->dir_created)
>> +			hccs_remove_die_dir(die);
>> +	}
>> +
>> +	kobject_put(&chip->kobj);
>> +}
>> +
>> +static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
>> +{
>> +	u8 i;
>> +
>> +	for (i = 0; i < hdev->chip_num; i++)
>> +		hccs_remove_chip_dir(&hdev->chips[i]);
>> +}
>> +
>> +static int hccs_create_hccs_dir(struct hccs_dev *hdev,
>> +				struct hccs_die_info *die,
>> +				struct hccs_port_info *port)
>> +{
>> +	int ret;
>> +
>> +	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
>> +				   &die->kobj, "hccs%d", port->port_id);
>> +	if (ret) {
>> +		kobject_put(&port->kobj);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_create_die_dir(struct hccs_dev *hdev,
>> +			       struct hccs_chip_info *chip,
>> +			       struct hccs_die_info *die)
>> +{
>> +	struct hccs_port_info *port;
>> +	int ret;
>> +	u16 i;
>> +
>> +	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
>> +				   &chip->kobj, "die%d", die->die_id);
>> +	if (ret) {
>> +		kobject_put(&die->kobj);
>> +		return ret;
>> +	}
>> +
>> +	for (i = 0; i < die->port_num; i++) {
>> +		port = &die->ports[i];
>> +		ret = hccs_create_hccs_dir(hdev, die, port);
>> +		if (ret) {
>> +			dev_err(hdev->dev, "create hccs%d dir failed.\n",
>> +				port->port_id);
>> +			goto err;
>> +		}
>> +		port->dir_created = true;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	hccs_remove_die_dir(die);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_create_chip_dir(struct hccs_dev *hdev,
>> +				struct hccs_chip_info *chip)
>> +{
>> +	struct hccs_die_info *die;
>> +	int ret;
>> +	u16 id;
>> +
>> +	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
>> +				   &hdev->dev->kobj, "chip%d", chip->chip_id);
>> +	if (ret) {
>> +		kobject_put(&chip->kobj);
>> +		return ret;
>> +	}
>> +
>> +	for (id = 0; id < chip->die_num; id++) {
>> +		die = &chip->dies[id];
>> +		ret = hccs_create_die_dir(hdev, chip, die);
>> +		if (ret)
>> +			goto err;
>> +		die->dir_created = true;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	hccs_remove_chip_dir(chip);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_create_topo_dirs(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_chip_info *chip;
>> +	u8 id, k;
>> +	int ret;
>> +
>> +	for (id = 0; id < hdev->chip_num; id++) {
>> +		chip = &hdev->chips[id];
>> +		ret = hccs_create_chip_dir(hdev, chip);
>> +		if (ret) {
>> +			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	for (k = 0; k < id; k++)
>> +		hccs_remove_chip_dir(&hdev->chips[k]);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_probe(struct platform_device *pdev)
>> +{
>> +	struct acpi_device *acpi_dev;
>> +	struct hccs_dev *hdev;
>> +	int rc;
>> +
>> +	pr_info("kunpeng_hccs is initializing.\n");
> Drop.
Ack.
>
>> +
>> +	if (acpi_disabled) {
>> +		dev_err(&pdev->dev, "acpi is disabled.\n");
> Is it possible for your ACPI-only driver?
Yes
>
>
>> +		return -ENODEV;
>> +	}
>> +	acpi_dev = ACPI_COMPANION(&pdev->dev);
>> +	if (!acpi_dev)
>> +		return -ENODEV;
>> +
>> +	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
>> +	if (!hdev)
>> +		return -ENOMEM;
>> +	hdev->acpi_dev = acpi_dev;
>> +	hdev->dev = &pdev->dev;
>> +	platform_set_drvdata(pdev, hdev);
>> +
>> +	rc = hccs_get_device_property(hdev);
>> +	if (rc)
>> +		return rc;
>> +
>> +	mutex_init(&hdev->lock);
>> +
>> +	rc = hccs_register_pcc_channel(hdev);
>> +	if (rc)
>> +		return rc;
>> +
>> +	rc = hccs_get_dev_caps(hdev);
>> +	if (rc)
>> +		goto err_uninit_mbox_client;
>> +
>> +	rc = hccs_get_hw_info(hdev);
>> +	if (rc)
>> +		goto err_uninit_mbox_client;
>> +
>> +	rc = hccs_create_topo_dirs(hdev);
>> +	if (rc)
>> +		goto err_uninit_mbox_client;
>> +
>> +	dev_info(&pdev->dev, "HISI HCCS driver registered!\n");
> Drop simple probe success msgs. There's already kernel infrastructure
> giving you this information.
All right, drop it.
>
>> +
>> +	return 0;
>> +
>> +err_uninit_mbox_client:
>> +	hccs_unregister_pcc_channel(hdev);
>> +
>> +	return rc;
>> +}
>> +
>> +static int hccs_remove(struct platform_device *pdev)
>> +{
>> +	struct hccs_dev *hdev = platform_get_drvdata(pdev);
>> +
>> +	hccs_remove_topo_dirs(hdev);
>> +	hccs_unregister_pcc_channel(hdev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct acpi_device_id hccs_acpi_match[] = {
>> +	{ "HISI04B1", },
>> +};
>> +
>> +static struct platform_driver hccs_driver = {
>> +	.probe = hccs_probe,
>> +	.remove = hccs_remove,
>> +	.driver = {
>> +		.name = "kunpeng_hccs",
>> +		.acpi_match_table = ACPI_PTR(hccs_acpi_match),
> Drop ACPI_PTR as it is not balanced with __maybe_unused.
ok, will drop it in v2.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25  3:04   ` lihuisong (C)
@ 2023-04-25  6:08     ` Arnd Bergmann
  2023-04-25  9:42       ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Arnd Bergmann @ 2023-04-25  6:08 UTC (permalink / raw)
  To: Huisong Li, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	huangdaode, linux-acpi, Len Brown, Rafael J . Wysocki,
	devicetree, Rob Herring, Frank Rowand, Krzysztof Kozlowski

On Tue, Apr 25, 2023, at 05:04, lihuisong (C) wrote:
> 在 2023/4/24 16:09, Arnd Bergmann 写道:
>> On Mon, Apr 24, 2023, at 09:30, Huisong Li wrote:

>>         depends on ACPI
>>         depends on (ARM64 && ARCH_HISI) || COMPILE_TEST
> What do you think of adjusting it as below?
> menu "Hisilicon SoC drivers"
>      depends on ARCH_HISI || COMPILE_TEST
>
> config KUNPENG_HCCS
>      depends on ACPI
>      depends on ARM64 || COMPILE_TEST

Yes, that's perfect.

>>
>>> +
>>> +#include "kunpeng_hccs.h"
>>> +
>>> +/* PCC defines */
>>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
>> Should these perhaps be in include/acpi/pcc.h? The 0x50434300
>> number is just "PCC\0", so it appears to not be HCCS specific.
> This is a PCC signature. As stated in the APCI,
> "The signature of a subspace is computed by a bitwiseor of the value 
> 0x50434300
> with the subspace ID. For example, subspace 3 has the signature 0x50434303."
>
> I am not sure if all driver need to use this fixed signature mask.
> As far as I know, cppc_acpi.c didn't use this signature and 
> xgene-hwmon.c used another mask defined in its driver.
> So I place it here.

I would still put it into the generic header, but it doesn't
really matter much, so do it whichever way you prefer. No need
for a separate patch if you decide to use the global header,
it can just be part of your normal patch.

>>> +
>>> +static int hccs_get_device_property(struct hccs_dev *hdev)
>>> +{
>>> +	struct device *dev = hdev->dev;
>>> +
>>> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
>>> +		dev_err(hdev->dev, "no device-flags property.\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
>>> +		dev_err(hdev->dev, "no pcc-type property.\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
>>> +		dev_err(hdev->dev, "no pcc-channel property.\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
>>> +	if (!hccs_dev_property_supported(hdev))
>>> +		return -EOPNOTSUPP;
>>> +
>> Where are the device properties documented? I'm never quite sure how
>> to handle these for ACPI-only drivers, since we don't normally have the
>> bindings in Documentation/devicetree/bindings/, but it feels like there
>> should be some properly reviewed document somewhere else.
> These are ACPI-only, instead of DT.
> I will add a comment here as Krzysztof suggested.

I understand that they are ACPI-only, what I'm more interested here is
the general question of how we should document them, to ensure these
are handled consistently across drivers.

>>> --- /dev/null
>>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.h
>>> @@ -0,0 +1,204 @@
>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>> +/* Copyright (c) 2023 Hisilicon Limited. */
>>> +
>>> +#ifndef __KUNPENG_HCCS_H__
>>> +#define __KUNPENG_HCCS_H__
>> Are you planning to add more drivers that share this file? If not,
>> just fold the contents into the driver itself.
> Yes, we will add more drivers in this file.

Ok.


       Arnd

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25  6:08     ` Arnd Bergmann
@ 2023-04-25  9:42       ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-04-25  9:42 UTC (permalink / raw)
  To: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	huangdaode, linux-acpi, Len Brown, Rafael J . Wysocki,
	devicetree, Rob Herring, Frank Rowand, Krzysztof Kozlowski


在 2023/4/25 14:08, Arnd Bergmann 写道:
> On Tue, Apr 25, 2023, at 05:04, lihuisong (C) wrote:
>> 在 2023/4/24 16:09, Arnd Bergmann 写道:
>>> On Mon, Apr 24, 2023, at 09:30, Huisong Li wrote:
>>>          depends on ACPI
>>>          depends on (ARM64 && ARCH_HISI) || COMPILE_TEST
>> What do you think of adjusting it as below?
>> menu "Hisilicon SoC drivers"
>>       depends on ARCH_HISI || COMPILE_TEST
>>
>> config KUNPENG_HCCS
>>       depends on ACPI
>>       depends on ARM64 || COMPILE_TEST
> Yes, that's perfect.
>
>>>> +
>>>> +#include "kunpeng_hccs.h"
>>>> +
>>>> +/* PCC defines */
>>>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>>>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
>>> Should these perhaps be in include/acpi/pcc.h? The 0x50434300
>>> number is just "PCC\0", so it appears to not be HCCS specific.
>> This is a PCC signature. As stated in the APCI,
>> "The signature of a subspace is computed by a bitwiseor of the value
>> 0x50434300
>> with the subspace ID. For example, subspace 3 has the signature 0x50434303."
>>
>> I am not sure if all driver need to use this fixed signature mask.
>> As far as I know, cppc_acpi.c didn't use this signature and
>> xgene-hwmon.c used another mask defined in its driver.
>> So I place it here.
> I would still put it into the generic header, but it doesn't
> really matter much, so do it whichever way you prefer. No need
> for a separate patch if you decide to use the global header,
> it can just be part of your normal patch.
ok, keep it the way it is now.
>
>>>> +
>>>> +static int hccs_get_device_property(struct hccs_dev *hdev)
>>>> +{
>>>> +	struct device *dev = hdev->dev;
>>>> +
>>>> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
>>>> +		dev_err(hdev->dev, "no device-flags property.\n");
>>>> +		return -ENODEV;
>>>> +	}
>>>> +
>>>> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
>>>> +		dev_err(hdev->dev, "no pcc-type property.\n");
>>>> +		return -ENODEV;
>>>> +	}
>>>> +
>>>> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
>>>> +		dev_err(hdev->dev, "no pcc-channel property.\n");
>>>> +		return -ENODEV;
>>>> +	}
>>>> +
>>>> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
>>>> +	if (!hccs_dev_property_supported(hdev))
>>>> +		return -EOPNOTSUPP;
>>>> +
>>> Where are the device properties documented? I'm never quite sure how
>>> to handle these for ACPI-only drivers, since we don't normally have the
>>> bindings in Documentation/devicetree/bindings/, but it feels like there
>>> should be some properly reviewed document somewhere else.
>> These are ACPI-only, instead of DT.
>> I will add a comment here as Krzysztof suggested.
> I understand that they are ACPI-only, what I'm more interested here is
> the general question of how we should document them, to ensure these
> are handled consistently across drivers.
These device properties are reported by ACPI table in firmware.
They are fixed in platform firmware. The user cannot modify them.
Do we need to document them?
>
>>>> --- /dev/null
>>>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.h
>>>> @@ -0,0 +1,204 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>>> +/* Copyright (c) 2023 Hisilicon Limited. */
>>>> +
>>>> +#ifndef __KUNPENG_HCCS_H__
>>>> +#define __KUNPENG_HCCS_H__
>>> Are you planning to add more drivers that share this file? If not,
>>> just fold the contents into the driver itself.
>> Yes, we will add more drivers in this file.
> Ok.
>
>
>         Arnd
> .

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  8:09 ` Arnd Bergmann
  2023-04-25  3:04   ` lihuisong (C)
@ 2023-04-25 10:30   ` Sudeep Holla
  2023-04-25 13:00     ` lihuisong (C)
  1 sibling, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-04-25 10:30 UTC (permalink / raw)
  To: Arnd Bergmann, Huisong Li
  Cc: Bjorn Andersson, Matthias Brugger, Sudeep Holla,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

Thanks Arnd for cc-ing the ALKML.

On Mon, Apr 24, 2023 at 10:09:47AM +0200, Arnd Bergmann wrote:
> On Mon, Apr 24, 2023, at 09:30, Huisong Li wrote:

[...]

> > +
> > +static int hccs_get_device_property(struct hccs_dev *hdev)
> > +{
> > +	struct device *dev = hdev->dev;
> > +
> > +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
> > +		dev_err(hdev->dev, "no device-flags property.\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
> > +		dev_err(hdev->dev, "no pcc-type property.\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
> > +		dev_err(hdev->dev, "no pcc-channel property.\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
> > +	if (!hccs_dev_property_supported(hdev))
> > +		return -EOPNOTSUPP;
> > +
>
> Where are the device properties documented? I'm never quite sure how
> to handle these for ACPI-only drivers, since we don't normally have the
> bindings in Documentation/devicetree/bindings/, but it feels like there
> should be some properly reviewed document somewhere else.
>
> Adding ACPI and devicetree maintainers to Cc for clarification.

Why are these DSD style properties added here ? Why can't we just make
use of _CRS with Generic Address Structure(GAS) register entry for each
of the PCC channel which eliminates the need of "pcc-chan-id". The type
must be deduced from the order in the list of _CRS if needed. I don't
quite understand what magic the flags contain here to provide any info
there.

--
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25  3:16   ` lihuisong (C)
@ 2023-04-25 11:24     ` Sudeep Holla
  0 siblings, 0 replies; 79+ messages in thread
From: Sudeep Holla @ 2023-04-25 11:24 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Krzysztof Kozlowski, andersson, matthias.bgg,
	angelogioacchino.delregno, shawnguo, arnd, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, Sudeep Holla, liuyonglong, huangdaode

On Tue, Apr 25, 2023 at 11:16:32AM +0800, lihuisong (C) wrote:
> Hi Krzysztof,
> Thanks for your review. My reply is as follows.
> 
> 在 2023/4/24 16:42, Krzysztof Kozlowski 写道:

[...]

> > Maybe leave a comment that all these are ACPI-only and are not allowed
> > in DT usage. Without bindings we are not going to review them. OTOH, I
> > think we don't have a process for such cases in general.
> Thanks for your suggestion. will add it.

Please look at my response and continue the discussion as why _CRS can't
be used[1] before you add any DT bindings. I would prefer to avoid any
DSD style properties especially if it is used only in ACPI.

-- 
Regards,
Sudeep

[1] https://lore.kernel.org/all/20230425103040.znv66k364ant6klq@bogus/

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25 10:30   ` Sudeep Holla
@ 2023-04-25 13:00     ` lihuisong (C)
  2023-04-25 13:19       ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-04-25 13:00 UTC (permalink / raw)
  To: Sudeep Holla, Arnd Bergmann
  Cc: Bjorn Andersson, Matthias Brugger, AngeloGioacchino Del Regno,
	Shawn Guo, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong, huangdaode, linux-acpi, Len Brown,
	Rafael J. Wysocki, devicetree, Rob Herring, Frank Rowand,
	Krzysztof Kozlowski


在 2023/4/25 18:30, Sudeep Holla 写道:
> Thanks Arnd for cc-ing the ALKML.
>
> On Mon, Apr 24, 2023 at 10:09:47AM +0200, Arnd Bergmann wrote:
>> On Mon, Apr 24, 2023, at 09:30, Huisong Li wrote:
> [...]
>
>>> +
>>> +static int hccs_get_device_property(struct hccs_dev *hdev)
>>> +{
>>> +	struct device *dev = hdev->dev;
>>> +
>>> +	if (device_property_read_u32(dev, "device-flags", &hdev->flags)) {
>>> +		dev_err(hdev->dev, "no device-flags property.\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	if (device_property_read_u8(dev, "pcc-type", &hdev->type)) {
>>> +		dev_err(hdev->dev, "no pcc-type property.\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	if (device_property_read_u32(dev, "pcc-chan-id", &hdev->chan_id)) {
>>> +		dev_err(hdev->dev, "no pcc-channel property.\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	hdev->intr_mode = hccs_get_bit(hdev->flags, HCCS_DEV_FLAGS_INTR_B);
>>> +	if (!hccs_dev_property_supported(hdev))
>>> +		return -EOPNOTSUPP;
>>> +
>> Where are the device properties documented? I'm never quite sure how
>> to handle these for ACPI-only drivers, since we don't normally have the
>> bindings in Documentation/devicetree/bindings/, but it feels like there
>> should be some properly reviewed document somewhere else.
>>
>> Adding ACPI and devicetree maintainers to Cc for clarification.
> Why are these DSD style properties added here ? Why can't we just make
> use of _CRS with Generic Address Structure(GAS) register entry for each
> of the PCC channel which eliminates the need of "pcc-chan-id". The type
> must be deduced from the order in the list of _CRS if needed. I don't
For firmware, DSD way is simpler and easier to manage these virtual 
platform devices,
and it's an usual way in kernel.
Driver only needs to get a fixed value, like pcc-id and type, here.

Any vantage if using _CRS with GAS compared with DSD?
> quite understand what magic the flags contain here to provide any info
> there.
This flag is used to report other properties, and every bit means a 
property.
For instance, driver doesn't need to request PCC channel during the 
probing phase if driver use PCC operation Region.
>
> .

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25 13:00     ` lihuisong (C)
@ 2023-04-25 13:19       ` Sudeep Holla
  2023-04-26 12:12         ` lihuisong (C)
  2023-05-04 13:16         ` lihuisong (C)
  0 siblings, 2 replies; 79+ messages in thread
From: Sudeep Holla @ 2023-04-25 13:19 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger, Sudeep Holla,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

On Tue, Apr 25, 2023 at 09:00:31PM +0800, lihuisong (C) wrote:
> 
> For firmware, DSD way is simpler and easier to manage these virtual platform
> devices, and it's an usual way in kernel.

Any specific examples you are referring here. We had lots of debate when
DSD was introduced. It must be used only when there is no standard ACPI
way to achieve the same. But in this I don't (yet) think that is the case.
Further "simplicity" is remotely not the reason why you must use DSD.
So until you provide me technical reasons as why _CRS can't work, I
have to NACK this approach. DSD in this case seems like pure hack.

> Driver only needs to get a fixed value, like pcc-id and type, here.
>

Yes and _CRS is used to get similar such properties in ACPI. It includes
normally MMIO and interrupts and since GAS supports PCC and _CRS can
contain GAS, you must simply use that.

> Any vantage if using _CRS with GAS compared with DSD?

Simple IMO, it is also existing standard to achieve things you are trying
to here and DSD is not. You are defining new properties to make DSD work.

So the real question is if _CRS can be used what is the point in defining
DSD for that. Unless I hear more technical and solid reasoning, I see
DSD as just hack and misuse here. It wasn't designed for that and must not
be allowed to make use of it for such use case.

Anyways in case we decide to take DSD route(after more deeper and technical
discussions), as in the kernel docs, please refer [1] for DSD. You need
to publish properties there so that no one comes up with similar but
alternate solution to do exactly this.

> > quite understand what magic the flags contain here to provide any info
> > there.
> This flag is used to report other properties, and every bit means a
> property.
> For instance, driver doesn't need to request PCC channel during the probing
> phase if driver use PCC operation Region.

Sorry I still don't understand it fully.

-- 
Regards,
Sudeep

[1] https://github.com/UEFI/DSD-Guide

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25 13:19       ` Sudeep Holla
@ 2023-04-26 12:12         ` lihuisong (C)
  2023-05-04 13:16         ` lihuisong (C)
  1 sibling, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-04-26 12:12 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/4/25 21:19, Sudeep Holla 写道:
> On Tue, Apr 25, 2023 at 09:00:31PM +0800, lihuisong (C) wrote:
>> For firmware, DSD way is simpler and easier to manage these virtual platform
>> devices, and it's an usual way in kernel.
> Any specific examples you are referring here. We had lots of debate when
> DSD was introduced. It must be used only when there is no standard ACPI
> way to achieve the same. But in this I don't (yet) think that is the case.
> Further "simplicity" is remotely not the reason why you must use DSD.
> So until you provide me technical reasons as why _CRS can't work, I
> have to NACK this approach. DSD in this case seems like pure hack.
>
>> Driver only needs to get a fixed value, like pcc-id and type, here.
>>
> Yes and _CRS is used to get similar such properties in ACPI. It includes
> normally MMIO and interrupts and since GAS supports PCC and _CRS can
> contain GAS, you must simply use that.
Hi Sudeep,
Can you give me some usage examples about this? I will try to do it.
>
>> Any vantage if using _CRS with GAS compared with DSD?
> Simple IMO, it is also existing standard to achieve things you are trying
> to here and DSD is not. You are defining new properties to make DSD work.
>
> So the real question is if _CRS can be used what is the point in defining
> DSD for that. Unless I hear more technical and solid reasoning, I see
> DSD as just hack and misuse here. It wasn't designed for that and must not
> be allowed to make use of it for such use case.
>
> Anyways in case we decide to take DSD route(after more deeper and technical
> discussions), as in the kernel docs, please refer [1] for DSD. You need
> to publish properties there so that no one comes up with similar but
> alternate solution to do exactly this.
All right.
>
>>> quite understand what magic the flags contain here to provide any info
>>> there.
>> This flag is used to report other properties, and every bit means a
>> property.
>> For instance, driver doesn't need to request PCC channel during the probing
>> phase if driver use PCC operation Region.
> Sorry I still don't understand it fully.
If a driver uses type2 with polling way on one platform, and uses PCC 
operation Region way to obtain some information on other platform.
The driver doesn't need to request PCC channel when it uses PCC 
Operation Region.
So this driver must be aware of the communication way in advance in a 
way during initialization.

>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-25 13:19       ` Sudeep Holla
  2023-04-26 12:12         ` lihuisong (C)
@ 2023-05-04 13:16         ` lihuisong (C)
  2023-05-15  3:37           ` lihuisong (C)
  2023-05-15 13:08           ` Sudeep Holla
  1 sibling, 2 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-05-04 13:16 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/4/25 21:19, Sudeep Holla 写道:
> On Tue, Apr 25, 2023 at 09:00:31PM +0800, lihuisong (C) wrote:
>> For firmware, DSD way is simpler and easier to manage these virtual platform
>> devices, and it's an usual way in kernel.
> Any specific examples you are referring here. We had lots of debate when
> DSD was introduced. It must be used only when there is no standard ACPI
> way to achieve the same. But in this I don't (yet) think that is the case.
> Further "simplicity" is remotely not the reason why you must use DSD.
> So until you provide me technical reasons as why _CRS can't work, I
> have to NACK this approach. DSD in this case seems like pure hack.
>
>> Driver only needs to get a fixed value, like pcc-id and type, here.
>>
> Yes and _CRS is used to get similar such properties in ACPI. It includes
> normally MMIO and interrupts and since GAS supports PCC and _CRS can
> contain GAS, you must simply use that.
Hi Sudeep,

I'm tring to use CRS with GAS to report PCC channel ID and get other 
informations driver need by address.
I found a way to obtain the generic register information according to 
"Referencing the PCC address space" in ACPI spec.
And driver also get the PCC generic register information successfully.

But I don't know how to set and use the address in PCC register.
Where should this address come from?
It seems that ACPI spec is not very detailed about this.
Do you have any suggestions?

On the other hand, we think that System Memory space + method can also 
achieve above goal.
What do you think of that?

Best regards,
Huisong

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-04 13:16         ` lihuisong (C)
@ 2023-05-15  3:37           ` lihuisong (C)
  2023-05-15 13:08           ` Sudeep Holla
  1 sibling, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-05-15  3:37 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/5/4 21:16, lihuisong (C) 写道:
>
> 在 2023/4/25 21:19, Sudeep Holla 写道:
>> On Tue, Apr 25, 2023 at 09:00:31PM +0800, lihuisong (C) wrote:
>>> For firmware, DSD way is simpler and easier to manage these virtual 
>>> platform
>>> devices, and it's an usual way in kernel.
>> Any specific examples you are referring here. We had lots of debate when
>> DSD was introduced. It must be used only when there is no standard ACPI
>> way to achieve the same. But in this I don't (yet) think that is the 
>> case.
>> Further "simplicity" is remotely not the reason why you must use DSD.
>> So until you provide me technical reasons as why _CRS can't work, I
>> have to NACK this approach. DSD in this case seems like pure hack.
>>
>>> Driver only needs to get a fixed value, like pcc-id and type, here.
>>>
>> Yes and _CRS is used to get similar such properties in ACPI. It includes
>> normally MMIO and interrupts and since GAS supports PCC and _CRS can
>> contain GAS, you must simply use that.
> Hi Sudeep,
>
> I'm tring to use CRS with GAS to report PCC channel ID and get other 
> informations driver need by address.
> I found a way to obtain the generic register information according to 
> "Referencing the PCC address space" in ACPI spec.
> And driver also get the PCC generic register information successfully.
>
> But I don't know how to set and use the address in PCC register.
> Where should this address come from?
> It seems that ACPI spec is not very detailed about this.
> Do you have any suggestions?
>
> On the other hand, we think that System Memory space + method can also 
> achieve above goal.
> What do you think of that?
>
>
Hi Sudeep,

Can you give us some suggestions about above question and idea?

Looking forward to your reply.

/Huisong

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-04 13:16         ` lihuisong (C)
  2023-05-15  3:37           ` lihuisong (C)
@ 2023-05-15 13:08           ` Sudeep Holla
  2023-05-16  7:35             ` lihuisong (C)
  1 sibling, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-15 13:08 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

On Thu, May 04, 2023 at 09:16:16PM +0800, lihuisong (C) wrote:
>
> I'm tring to use CRS with GAS to report PCC channel ID and get other
> informations driver need by address.

OK you had pcc-chan-id pcc-type and device-flags in the DSD style bindings
to begin with. I haven't understood device-flags here so can't comment on
that.

> I found a way to obtain the generic register information according to
> "Referencing the PCC address space" in ACPI spec.
> And driver also get the PCC generic register information successfully.
>

Can you elaborate ? I assume by that you must be able to get pcc-chan-id
right ? You must not need pcc-type as the pcc mailbox driver must handle
the type for you. If not, we may need to fix or add any missing support.

> But I don't know how to set and use the address in PCC register.

It must be same as what you would have specified in you new bindings
under "pcc-chan-id". I am confused as you say you were able to get the
PCC generic register information successfully but you still claim you
don't know how to set or use the address.

> Where should this address come from?
> It seems that ACPI spec is not very detailed about this.
> Do you have any suggestions?
>

I am afraid, I don't have any as I am failing to understand the exact issue
you are facing. 

Let me try to ask the question explicity here: 

If you are just referring to just the <RegisterAddress,> in

Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)

then,

RegisterAddress is usually the offset in the comms address associated with
the PCC subspace ID specified in AccessSize. Yes the use of AccessSize for
the PCC subspace ID is bit confusing though.

You can either list all the registers with _CRS individually or the driver
can just use the PCC subspace ID in AccessSize and keep RegisterAddress = 0
but access individual offset based on its own knowledge. I haven't seen the
full driver yet but I assuming that's how you would have used if you went with
your DSD pcc-chan-id proposal.

> On the other hand, we think that System Memory space + method can also
> achieve above goal. What do you think of that?

Again I don't understand what you mean by that.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-15 13:08           ` Sudeep Holla
@ 2023-05-16  7:35             ` lihuisong (C)
  2023-05-16 12:29               ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-16  7:35 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/5/15 21:08, Sudeep Holla 写道:
> On Thu, May 04, 2023 at 09:16:16PM +0800, lihuisong (C) wrote:
>> I'm tring to use CRS with GAS to report PCC channel ID and get other
>> informations driver need by address.
> OK you had pcc-chan-id pcc-type and device-flags in the DSD style bindings
> to begin with. I haven't understood device-flags here so can't comment on
> that.

We want to use the 'device-flags' to report some information by bit.
Currently, this driver requests PCC channel and use type2 to communicate 
with firmware.
But, if some platform support type3 and PCC Operation Region, driver can 
choice this method to communicate with firmware.
So firmware and driver have to use this flag to make compatibility.

>
>> I found a way to obtain the generic register information according to
>> "Referencing the PCC address space" in ACPI spec.
>> And driver also get the PCC generic register information successfully.
>>
> Can you elaborate ? I assume by that you must be able to get pcc-chan-id

Yes,driver can get pcc-chan-id by below register.

Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)

> right ? You must not need pcc-type as the pcc mailbox driver must handle
> the type for you. If not, we may need to fix or add any missing support.
Yes, PCC driver doesn't support it currently. And aother patch [1] we've 
been talking about does it.
If it is applied to kernel, we can drop this pcc-type here.

[1] 
https://patchwork.kernel.org/project/linux-acpi/patch/20230423110335.2679-2-lihuisong@huawei.com/
>
>> But I don't know how to set and use the address in PCC register.
> It must be same as what you would have specified in you new bindings
> under "pcc-chan-id". I am confused as you say you were able to get the
> PCC generic register information successfully but you still claim you
> don't know how to set or use the address.
My confusion about this address is mentioned below.
>> Where should this address come from?
>> It seems that ACPI spec is not very detailed about this.
>> Do you have any suggestions?
>>
> I am afraid, I don't have any as I am failing to understand the exact issue
> you are facing.
>
> Let me try to ask the question explicity here:
>
> If you are just referring to just the <RegisterAddress,> in
>
> Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)
Yeah, this is what I'm using.
>
> then,
>
> RegisterAddress is usually the offset in the comms address associated with
Communication subspace in share memory of PCC subspace?
> the PCC subspace ID specified in AccessSize. Yes the use of AccessSize for
> the PCC subspace ID is bit confusing though.
>
> You can either list all the registers with _CRS individually or the driver
List all the registers as following way?
Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
{
     QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, 
NonCacheable, ReadWrite,
         0x0000000000000000, // Granularity
         0x0000000098190000, // Range Minimum
         0x000000009819FFFF, // Range Maximum
         0x0000000000000000, // Translation Offset
         0x0000000000010000, // Length
         ,, , AddressRangeMemory, TypeStatic)
})
> can just use the PCC subspace ID in AccessSize and keep RegisterAddress = 0
> but access individual offset based on its own knowledge. I haven't seen the
Following words come from ACPI spec.
-->
As an example, the following resource template refers to the feld 
occupying bits 8 through 15 at address 0x30 in PCC
subspace 9:
ResourceTemplate()
{
Register (
     PCC, //AddressSpaceKeyword
     8, //RegisterBitWidth
     8, //RegisterBitOffset
     0x30, //RegisterAddress
     9 //AccessSize (subspace ID)
     )
}

If the width of the address is 32bit, set RegisterAddress to 0, 
RegisterBitOffset to 0 and set RegisterBitWidth to 64 here.
Driver can access to the ((void __iomem *)pcc_comm_addr + 0x8 + 0) and 
((void __iomem *)pcc_comm_addr + 0x8 + 4) address,right?
(This virtual address = pcc mapped address + header size + offset within 
PCC subspace.)
> full driver yet but I assuming that's how you would have used if you went with
> your DSD pcc-chan-id proposal.
>
>> On the other hand, we think that System Memory space + method can also
>> achieve above goal. What do you think of that?
> Again I don't understand what you mean by that.
Sorry, here is what I want to say.
-->
OperationRegion (CCS0, SystemMemory, 0x00000002081000CC, 0x04)
Field (CCS0, DWordAcc, NoLock, Preserve)
{
     HAU1,   32
}
OperationRegion (CCS1, SystemMemory, 0x0000000201070410, 0x04)
Field (CCS1, DWordAcc, NoLock, Preserve)
{
     HCGE,   32
}
Method (_DSM, 2, Serialized)  // _DSM: Device-Specific Method
{
     If ((Arg0 == ToUUID ("b06b81ab-0134-4a45-9b0c-483447b95fa7")))
     {
         If ((Arg1 == One))
         {
             Return (HAU1)
         }

         Return (HCGE)
     }
}

Driver can call _DSM method to get some information, such as pcc_chan_id 
and device_flags.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-16  7:35             ` lihuisong (C)
@ 2023-05-16 12:29               ` Sudeep Holla
  2023-05-16 14:13                 ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-16 12:29 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger, Sudeep Holla,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

On Tue, May 16, 2023 at 03:35:54PM +0800, lihuisong (C) wrote:
>
> 在 2023/5/15 21:08, Sudeep Holla 写道:
> > On Thu, May 04, 2023 at 09:16:16PM +0800, lihuisong (C) wrote:
> > > I'm tring to use CRS with GAS to report PCC channel ID and get other
> > > informations driver need by address.
> > OK you had pcc-chan-id pcc-type and device-flags in the DSD style bindings
> > to begin with. I haven't understood device-flags here so can't comment on
> > that.
>
> We want to use the 'device-flags' to report some information by bit.

Please give more details, until then NACK for the idea.

> Currently, this driver requests PCC channel and use type2 to communicate
> with firmware.

OKAY...

> But, if some platform support type3 and PCC Operation Region, driver can
> choice this method to communicate with firmware.
> So firmware and driver have to use this flag to make compatibility.
>

I would rather add such things to the spec if it is any sort of limitation
with the current specification.

> >
> > > I found a way to obtain the generic register information according to
> > > "Referencing the PCC address space" in ACPI spec.
> > > And driver also get the PCC generic register information successfully.
> > >
> > Can you elaborate ? I assume by that you must be able to get pcc-chan-id
>
> Yes,driver can get pcc-chan-id by below register.
>
> Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)
>

Good to know.

> > right ? You must not need pcc-type as the pcc mailbox driver must handle
> > the type for you. If not, we may need to fix or add any missing support.
> Yes, PCC driver doesn't support it currently. And aother patch [1] we've
> been talking about does it.
> If it is applied to kernel, we can drop this pcc-type here.
>
> [1] https://patchwork.kernel.org/project/linux-acpi/patch/20230423110335.2679-2-lihuisong@huawei.com/

OK then we are good, no need for pcc-type then ?

> >
> > > But I don't know how to set and use the address in PCC register.
> > It must be same as what you would have specified in you new bindings
> > under "pcc-chan-id". I am confused as you say you were able to get the
> > PCC generic register information successfully but you still claim you
> > don't know how to set or use the address.
> My confusion about this address is mentioned below.

OK

> > > Where should this address come from?
> > > It seems that ACPI spec is not very detailed about this.
> > > Do you have any suggestions?
> > >
> > I am afraid, I don't have any as I am failing to understand the exact issue
> > you are facing.
> >
> > Let me try to ask the question explicity here:
> >
> > If you are just referring to just the <RegisterAddress,> in
> >
> > Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)
> Yeah, this is what I'm using.
> >
> > then,
> >
> > RegisterAddress is usually the offset in the comms address associated with
> Communication subspace in share memory of PCC subspace?
> > the PCC subspace ID specified in AccessSize. Yes the use of AccessSize for
> > the PCC subspace ID is bit confusing though.
> >
> > You can either list all the registers with _CRS individually or the driver
> List all the registers as following way?
> Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
> {
>     QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed,
> NonCacheable, ReadWrite,
>         0x0000000000000000, // Granularity
>         0x0000000098190000, // Range Minimum
>         0x000000009819FFFF, // Range Maximum
>         0x0000000000000000, // Translation Offset
>         0x0000000000010000, // Length
>         ,, , AddressRangeMemory, TypeStatic)
> })

Not sure if you can use QWordMemory here TBH.

> > can just use the PCC subspace ID in AccessSize and keep RegisterAddress = 0
> > but access individual offset based on its own knowledge. I haven't seen the
> Following words come from ACPI spec.
> -->
> As an example, the following resource template refers to the feld occupying
> bits 8 through 15 at address 0x30 in PCC
> subspace 9:
> ResourceTemplate()
> {
> Register (
>     PCC, //AddressSpaceKeyword
>     8, //RegisterBitWidth
>     8, //RegisterBitOffset
>    \x06pcc 0x30, //RegisterAddress
>     9 //AccessSize (subspace ID)
>     )
> }
>
> If the width of the address is 32bit, set RegisterAddress to 0,
> RegisterBitOffset to 0 and set RegisterBitWidth to 64 here.
> Driver can access to the ((void __iomem *)pcc_comm_addr + 0x8 + 0) and
> ((void __iomem *)pcc_comm_addr + 0x8 + 4) address,right?
> (This virtual address = pcc mapped address + header size + offset within PCC
> subspace.)

Yes that's my understanding. I remember seeing the driver is just fetching
pcc-chan-id using DSD style key-value pair, which means you don't need
any other info other than the PCC subspace/channel ID, just have address
as 0.

Also I see the driver uses type for just rejecting the type 3 PCCT. The
question is will the driver probe and run on a platform with type 3 PCCT ?
If so what is the problem running on such a platform. I see it is useless
check in the driver and can be dropped. Also the comment above enum
HCCS_DEV_FLAGS_INTR_B is confusing and  so is the way flags is used.

> > full driver yet but I assuming that's how you would have used if you went with
> > your DSD pcc-chan-id proposal.
> >
> > > On the other hand, we think that System Memory space + method can also
> > > achieve above goal. What do you think of that?
> > Again I don't understand what you mean by that.
> Sorry, here is what I want to say.
> -->
> OperationRegion (CCS0, SystemMemory, 0x00000002081000CC, 0x04)
> Field (CCS0, DWordAcc, NoLock, Preserve)
> {
>     HAU1,   32
> }
> OperationRegion (CCS1, SystemMemory, 0x0000000201070410, 0x04)
> Field (CCS1, DWordAcc, NoLock, Preserve)
> {
>     HCGE,   32
> }
> Method (_DSM, 2, Serialized)  // _DSM: Device-Specific Method
> {
>     If ((Arg0 == ToUUID ("b06b81ab-0134-4a45-9b0c-483447b95fa7")))
>     {
>         If ((Arg1 == One))
>         {
>             Return (HAU1)
>         }
>
>         Return (HCGE)
>     }
> }
>
> Driver can call _DSM method to get some information, such as pcc_chan_id and
> device_flags.

Big fat NACK for _DSM for the above purpose, please stop abusing _DSM or _DSD
for such information which can be obtained with the existing _CRS.

--
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-16 12:29               ` Sudeep Holla
@ 2023-05-16 14:13                 ` lihuisong (C)
  2023-05-16 14:35                   ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-16 14:13 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

Hi Sudeep,

Thanks for your reply.


在 2023/5/16 20:29, Sudeep Holla 写道:
> On Tue, May 16, 2023 at 03:35:54PM +0800, lihuisong (C) wrote:
>> 在 2023/5/15 21:08, Sudeep Holla 写道:
>>> On Thu, May 04, 2023 at 09:16:16PM +0800, lihuisong (C) wrote:
>>>> I'm tring to use CRS with GAS to report PCC channel ID and get other
>>>> informations driver need by address.
>>> OK you had pcc-chan-id pcc-type and device-flags in the DSD style bindings
>>> to begin with. I haven't understood device-flags here so can't comment on
>>> that.
>> We want to use the 'device-flags' to report some information by bit.
> Please give more details, until then NACK for the idea.
ok.
>
>> Currently, this driver requests PCC channel and use type2 to communicate
>> with firmware.
> OKAY...
>
>> But, if some platform support type3 and PCC Operation Region, driver can
>> choice this method to communicate with firmware.
>> So firmware and driver have to use this flag to make compatibility.
>>
> I would rather add such things to the spec if it is any sort of limitation
> with the current specification.
Agreed. but I think there isn't any limitation for this with the current 
specification.
There is no strong connection between PCC Operation Region and type3.
Driver can also use PCC to communicate with platform even if the type is 
type3.
In other words, it depends on driver's choice.

Here, we want to use one bit in device-flags to indicates that
firmware and driver use PCC operation Region on feature platform instead 
of only using PCC.

Sorry, I have to admit that the implementation of this driver about this 
really needs
to be optimized to make it more clear.
>
>>>> I found a way to obtain the generic register information according to
>>>> "Referencing the PCC address space" in ACPI spec.
>>>> And driver also get the PCC generic register information successfully.
>>>>
>>> Can you elaborate ? I assume by that you must be able to get pcc-chan-id
>> Yes,driver can get pcc-chan-id by below register.
>>
>> Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)
>>
> Good to know.
>
>>> right ? You must not need pcc-type as the pcc mailbox driver must handle
>>> the type for you. If not, we may need to fix or add any missing support.
>> Yes, PCC driver doesn't support it currently. And aother patch [1] we've
>> been talking about does it.
>> If it is applied to kernel, we can drop this pcc-type here.
>>
>> [1] https://patchwork.kernel.org/project/linux-acpi/patch/20230423110335.2679-2-lihuisong@huawei.com/
> OK then we are good, no need for pcc-type then ?
If driver may support more PCC types, the type also need be known by driver.
Because not all types have the same header size.
The PCC type can be obtained from the PCCT by requesting PCC channel.
 From this point, this pcc type here is unnecessary, right?
>
>>>> But I don't know how to set and use the address in PCC register.
>>> It must be same as what you would have specified in you new bindings
>>> under "pcc-chan-id". I am confused as you say you were able to get the
>>> PCC generic register information successfully but you still claim you
>>> don't know how to set or use the address.
>> My confusion about this address is mentioned below.
> OK
>
>>>> Where should this address come from?
>>>> It seems that ACPI spec is not very detailed about this.
>>>> Do you have any suggestions?
>>>>
>>> I am afraid, I don't have any as I am failing to understand the exact issue
>>> you are facing.
>>>
>>> Let me try to ask the question explicity here:
>>>
>>> If you are just referring to just the <RegisterAddress,> in
>>>
>>> Register (PCC, RegisterBitWidth, RegisterBitOffset, RegisterAddress, AccessSize)
>> Yeah, this is what I'm using.
>>> then,
>>>
>>> RegisterAddress is usually the offset in the comms address associated with
>> Communication subspace in share memory of PCC subspace?
>>> the PCC subspace ID specified in AccessSize. Yes the use of AccessSize for
>>> the PCC subspace ID is bit confusing though.
>>>
>>> You can either list all the registers with _CRS individually or the driver
>> List all the registers as following way?
>> Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
>> {
>>      QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed,
>> NonCacheable, ReadWrite,
>>          0x0000000000000000, // Granularity
>>          0x0000000098190000, // Range Minimum
>>          0x000000009819FFFF, // Range Maximum
>>          0x0000000000000000, // Translation Offset
>>          0x0000000000010000, // Length
>>          ,, , AddressRangeMemory, TypeStatic)
>> })
> Not sure if you can use QWordMemory here TBH.
Above way is what you say?
>
>>> can just use the PCC subspace ID in AccessSize and keep RegisterAddress = 0
>>> but access individual offset based on its own knowledge. I haven't seen the
>> Following words come from ACPI spec.
>> -->
>> As an example, the following resource template refers to the feld occupying
>> bits 8 through 15 at address 0x30 in PCC
>> subspace 9:
>> ResourceTemplate()
>> {
>> Register (
>>      PCC, //AddressSpaceKeyword
>>      8, //RegisterBitWidth
>>      8, //RegisterBitOffset
>>     \x06pcc 0x30, //RegisterAddress
>>      9 //AccessSize (subspace ID)
>>      )
>> }
>>
>> If the width of the address is 32bit, set RegisterAddress to 0,
>> RegisterBitOffset to 0 and set RegisterBitWidth to 64 here.
>> Driver can access to the ((void __iomem *)pcc_comm_addr + 0x8 + 0) and
>> ((void __iomem *)pcc_comm_addr + 0x8 + 4) address,right?
>> (This virtual address = pcc mapped address + header size + offset within PCC
>> subspace.)
> Yes that's my understanding. I remember seeing the driver is just fetching
> pcc-chan-id using DSD style key-value pair, which means you don't need
> any other info other than the PCC subspace/channel ID, just have address
> as 0.
But I still need the device-flags to report if use PCC operation Region.
If so I have to dig one address register from comm subspace, right?
>
> Also I see the driver uses type for just rejecting the type 3 PCCT. The
> question is will the driver probe and run on a platform with type 3 PCCT ?
Yes,some platforms may use PCC operation Region.
> If so what is the problem running on such a platform. I see it is useless
I didn't found any problems. But this driver should consider the 
possibility above mentioned.
> check in the driver and can be dropped. Also the comment above enum
> HCCS_DEV_FLAGS_INTR_B is confusing and  so is the way flags is used.
Thanks for you bringing it up.
Indeed, this HCCS_DEV_FLAGS_INTR_B is not good.
I'm going to replace it with PCC operation Region flag.
>
>>> full driver yet but I assuming that's how you would have used if you went with
>>> your DSD pcc-chan-id proposal.
>>>
>>>> On the other hand, we think that System Memory space + method can also
>>>> achieve above goal. What do you think of that?
>>> Again I don't understand what you mean by that.
>> Sorry, here is what I want to say.
>> -->
>> OperationRegion (CCS0, SystemMemory, 0x00000002081000CC, 0x04)
>> Field (CCS0, DWordAcc, NoLock, Preserve)
>> {
>>      HAU1,   32
>> }
>> OperationRegion (CCS1, SystemMemory, 0x0000000201070410, 0x04)
>> Field (CCS1, DWordAcc, NoLock, Preserve)
>> {
>>      HCGE,   32
>> }
>> Method (_DSM, 2, Serialized)  // _DSM: Device-Specific Method
>> {
>>      If ((Arg0 == ToUUID ("b06b81ab-0134-4a45-9b0c-483447b95fa7")))
>>      {
>>          If ((Arg1 == One))
>>          {
>>              Return (HAU1)
>>          }
>>
>>          Return (HCGE)
>>      }
>> }
>>
>> Driver can call _DSM method to get some information, such as pcc_chan_id and
>> device_flags.
> Big fat NACK for _DSM for the above purpose, please stop abusing _DSM or _DSD
> for such information which can be obtained with the existing _CRS.
Get it. Thanks.
> .

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-16 14:13                 ` lihuisong (C)
@ 2023-05-16 14:35                   ` Sudeep Holla
  2023-05-17  7:16                     ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-16 14:35 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, Sudeep Holla,
	linux-acpi, Len Brown, Rafael J. Wysocki, devicetree,
	Rob Herring, Frank Rowand, Krzysztof Kozlowski

On Tue, May 16, 2023 at 10:13:58PM +0800, lihuisong (C) wrote:
[...]

>
> But I still need the device-flags to report if use PCC operation Region.
> If so I have to dig one address register from comm subspace, right?

[...]

> Thanks for you bringing it up.
> Indeed, this HCCS_DEV_FLAGS_INTR_B is not good.
> I'm going to replace it with PCC operation Region flag.

From the above 2, I am getting a sense that all these flags dance is for
sharing a PCC subspace ID between this driver and the firmware PCC Opregion ?
If so that may not work as the current implementation of PCC Opregion
assumes the exclusive access to the channel. Since it is initialised
quite early, Opregion must succeed to get the mbox channel acquired and
this driver must fail if they are sharing the channel. Making the sharing
across firmware and this driver may need changes in the PCC Opregion
support code. One possible way is to acquire and release the channel for
each transaction which will be definitely overhead.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-16 14:35                   ` Sudeep Holla
@ 2023-05-17  7:16                     ` lihuisong (C)
  2023-05-17  9:30                       ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-17  7:16 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/5/16 22:35, Sudeep Holla 写道:
> On Tue, May 16, 2023 at 10:13:58PM +0800, lihuisong (C) wrote:
> [...]
>
>> But I still need the device-flags to report if use PCC operation Region.
>> If so I have to dig one address register from comm subspace, right?
> [...]
>
>> Thanks for you bringing it up.
>> Indeed, this HCCS_DEV_FLAGS_INTR_B is not good.
>> I'm going to replace it with PCC operation Region flag.
> >From the above 2, I am getting a sense that all these flags dance is for
> sharing a PCC subspace ID between this driver and the firmware PCC Opregion ?
No. I want to use this flag to make compability between different platforms.
This driver only use PCC OpRegion to access to the channel if platform 
support use PCC OpRegion.
Driver must select one of them (PCC and PCC OpRegion) to communicate 
with firmware on one platform.
> If so that may not work as the current implementation of PCC Opregion
> assumes the exclusive access to the channel. Since it is initialised
> quite early, Opregion must succeed to get the mbox channel acquired and
> this driver must fail if they are sharing the channel. Making the sharing
> across firmware and this driver may need changes in the PCC Opregion
Only using PCC OpRegion after requesting and releasing PCC channel 
shouldn't change PCC OpRegion code?
> support code. One possible way is to acquire and release the channel for
> each transaction which will be definitely overhead.
Yes, transaction will be definitely overhead.
The following method should be no such problem.
-->
If driver want to obtain other info by RegisterAddress and offset in PCC 
Register(),
driver generally needs to do it as follows:
1> get channel ID and RegisterAddress and offset.
2> call pcc_mbox_request_channel to acquire the channel.
3> ioremap 'shmem_base_addr' to get 'pcc_comm_addr'
4> obtain other info based on RegisterAddress, offset and 'pcc_comm_addr'.
If driver selects PCC OpRegion method, driver may also need to release 
this PCC channel by calling pcc_mbox_free_channel.
Because this channel will be requested when PCC OpRegion method is 
executed for the first time.


Overall, the above process is a bit cumbersome if this driver only use 
PCC OpRegion.
In addition, I have to dig one address from comm space in share memory,
which will cause the available size of comm space to decrease, right?
So it is better to use other way to do get channel ID and other info if 
it is possible.
What do you think?
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-17  7:16                     ` lihuisong (C)
@ 2023-05-17  9:30                       ` Sudeep Holla
  2023-05-17 11:35                         ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-17  9:30 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Sudeep Holla,
	Rob Herring, Frank Rowand, Krzysztof Kozlowski

On Wed, May 17, 2023 at 03:16:12PM +0800, lihuisong (C) wrote:

[...]

> No. I want to use this flag to make compability between different platforms.
> This driver only use PCC OpRegion to access to the channel if platform
> support use PCC OpRegion.

What do you mean by that ? It is not correct. If there is a PCC Opregion,
then you need to make it work with drivers/acpi/acpi_pcc.c

You need to have all the other details in the firmware(ASL). By looking
at the driver, it has no connection to PCC Opregion IMO unless I am missing
something.

> Driver must select one of them (PCC and PCC OpRegion) to communicate with
> firmware on one platform.

No for reasons mentioned above. PCC Opregion support in the kernel will
be minimal and already there. Fix that if it is not working. If you are
attempting to do something with PCC Opregion in this driver, it is just
wrong and I will NACK it if I see anything around that.

> > If so that may not work as the current implementation of PCC Opregion
> > assumes the exclusive access to the channel. Since it is initialised
> > quite early, Opregion must succeed to get the mbox channel acquired and
> > this driver must fail if they are sharing the channel. Making the sharing
> > across firmware and this driver may need changes in the PCC Opregion
> Only using PCC OpRegion after requesting and releasing PCC channel shouldn't
> change PCC OpRegion code?

I don't understand what exactly that means. The spec states clearly that
PCC subspaces that are used for PCC Operation Regions must not be used
as PCC subspaces for other std ACPI features. I don't understand what
really is going on, on this platform as I don't see what you are saying
(which is wrong and I disagree with approach) in the code posted yet.

> > support code. One possible way is to acquire and release the channel for
> > each transaction which will be definitely overhead.
> Yes, transaction will be definitely overhead.
> The following method should be no such problem.
> -->
> If driver want to obtain other info by RegisterAddress and offset in PCC
> Register(), driver generally needs to do it as follows:
> 1> get channel ID and RegisterAddress and offset.
> 2> call pcc_mbox_request_channel to acquire the channel.
> 3> ioremap 'shmem_base_addr' to get 'pcc_comm_addr'
> 4> obtain other info based on RegisterAddress, offset and 'pcc_comm_addr'.

Above sound good but it is not PCC Opregion. Either you are not giving
full context or you are confusing what PCC Opregion means. There is a
section "Declaring PCC Operation Regions", please refer and see if that
is what you have on your platform.

> If driver selects PCC OpRegion method, driver may also need to release this
> PCC channel by calling pcc_mbox_free_channel.

As I mentioned, the driver must not do anything related to PCC Opregion.

> Because this channel will be requested when PCC OpRegion method is executed
> for the first time.
>

drivers/acpi/acpi_pcc.c must take care of that. If not patch that and get
it working. It must be generic and nothing to do with your platform.

> 
> Overall, the above process is a bit cumbersome if this driver only use PCC
> OpRegion.

Yes and hence must not touch anything around PCC Opregion.

> In addition, I have to dig one address from comm space in share memory,
> which will cause the available size of comm space to decrease, right?
> So it is better to use other way to do get channel ID and other info if it
> is possible.
> What do you think?

I am more confused about your platform than yesterday, so I don't have
much valuable suggestions ATM.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-17  9:30                       ` Sudeep Holla
@ 2023-05-17 11:35                         ` lihuisong (C)
  2023-05-17 13:16                           ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-17 11:35 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/5/17 17:30, Sudeep Holla 写道:
> On Wed, May 17, 2023 at 03:16:12PM +0800, lihuisong (C) wrote:
>
> [...]
>
>> No. I want to use this flag to make compability between different platforms.
>> This driver only use PCC OpRegion to access to the channel if platform
>> support use PCC OpRegion.
> What do you mean by that ? It is not correct. If there is a PCC Opregion,
> then you need to make it work with drivers/acpi/acpi_pcc.c
>
> You need to have all the other details in the firmware(ASL). By looking
> at the driver, it has no connection to PCC Opregion IMO unless I am missing
> something.
Driver just needs to call these APIs, such as acpi_evaluate_integer(), 
if want to use PCC OpRegion.
I know that. I have tested PCC OpRegion before.
You've completely misunderstood what I said.😅

I mean that this driver plans to support both PCC and PCC OpRegion.
For example,
Platform A: this driver use PCC (as the current implementation)
Platform B: this driver use PCC OpRegion (Currently, this patch does not 
implement it, but it may be available in the future.)
Note:
This driver selects only one of them (PCC and PCC OpRegion) to 
communicate with firmware on one platform.
We use one bit in device-flags to know which one this driver will use.

I'm not sure if you can understand what I mean by saing that.
If you're not confused about this now, can you reply to my last email 
again?😁
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-17 11:35                         ` lihuisong (C)
@ 2023-05-17 13:16                           ` Sudeep Holla
  2023-05-18  8:24                             ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-17 13:16 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

On Wed, May 17, 2023 at 07:35:25PM +0800, lihuisong (C) wrote:
> 
> 在 2023/5/17 17:30, Sudeep Holla 写道:
> > On Wed, May 17, 2023 at 03:16:12PM +0800, lihuisong (C) wrote:
> > 
> > [...]
> > 
> > > No. I want to use this flag to make compability between different platforms.
> > > This driver only use PCC OpRegion to access to the channel if platform
> > > support use PCC OpRegion.
> > What do you mean by that ? It is not correct. If there is a PCC Opregion,
> > then you need to make it work with drivers/acpi/acpi_pcc.c
> > 
> > You need to have all the other details in the firmware(ASL). By looking
> > at the driver, it has no connection to PCC Opregion IMO unless I am missing
> > something.
> Driver just needs to call these APIs, such as acpi_evaluate_integer(), if
> want to use PCC OpRegion.

OK, please provide examples. I am definitely lost as it doesn't match with
my understanding of how PCC Opregions are/can be used.

> I know that. I have tested PCC OpRegion before.

Cool, examples please.

> You've completely misunderstood what I said.😅
>

Hmm, may be but I need examples.

> I mean that this driver plans to support both PCC and PCC OpRegion.
> For example,
> Platform A: this driver use PCC (as the current implementation)

Good, then just keep what it needs in the implementation nothing more
until you add support for something you have described below(not that
I agree, just want you to make progress here based on what is actually
required today)

> Platform B: this driver use PCC OpRegion (Currently, this patch does not
> implement it, but it may be available in the future.)

Then let us discuss that in the future, don't add unnecessary complexity
for some future use case today. You can always add it when you introduce
that feature or support in the future.

> Note:
> This driver selects only one of them (PCC and PCC OpRegion) to communicate
> with firmware on one platform.

Let us keep it simple(KISS). The driver works just for PCC not PCC Opregion
for now.

> We use one bit in device-flags to know which one this driver will use.
>

NACK again just to re-iterate my point if you have not yet accepted that
fact.

> I'm not sure if you can understand what I mean by saing that.
> If you're not confused about this now, can you reply to my last email
> again?😁
>

The example you had IIRC is use of System Memory Opregion to demonstrate
some _DSM. That has nothing to do with PCC Opregion.

Commit 77e2a04745ff ("ACPI: PCC: Implement OperationRegion handler for
the PCC Type 3 subtype") has the example in the commit message. IIRC,
you have even fixed couple of bugs in that driver. That is the reason
why I don't understand how you think this driver and that can or must
work together. At least I fail to see how ATM(examples please, by that
I mean ASL snippet for PCC vs PCC Opregion usage to work with this driver)

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-17 13:16                           ` Sudeep Holla
@ 2023-05-18  8:24                             ` lihuisong (C)
  2023-05-18  8:38                               ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-18  8:24 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/5/17 21:16, Sudeep Holla 写道:
> On Wed, May 17, 2023 at 07:35:25PM +0800, lihuisong (C) wrote:
>> 在 2023/5/17 17:30, Sudeep Holla 写道:
>>> On Wed, May 17, 2023 at 03:16:12PM +0800, lihuisong (C) wrote:
>>>
>>> [...]
>>>
>>>> No. I want to use this flag to make compability between different platforms.
>>>> This driver only use PCC OpRegion to access to the channel if platform
>>>> support use PCC OpRegion.
>>> What do you mean by that ? It is not correct. If there is a PCC Opregion,
>>> then you need to make it work with drivers/acpi/acpi_pcc.c
>>>
>>> You need to have all the other details in the firmware(ASL). By looking
>>> at the driver, it has no connection to PCC Opregion IMO unless I am missing
>>> something.
>> Driver just needs to call these APIs, such as acpi_evaluate_integer(), if
>> want to use PCC OpRegion.
> OK, please provide examples. I am definitely lost as it doesn't match with
> my understanding of how PCC Opregions are/can be used.
>
>> I know that. I have tested PCC OpRegion before.
> Cool, examples please.
>
>> You've completely misunderstood what I said.😅
>>
> Hmm, may be but I need examples.
As you said below, the driver works just for PCC not PCC Opregion for now.
not sure if we need to discuss how PCC Opregion is used here.
>
>> I mean that this driver plans to support both PCC and PCC OpRegion.
>> For example,
>> Platform A: this driver use PCC (as the current implementation)
> Good, then just keep what it needs in the implementation nothing more
> until you add support for something you have described below(not that
> I agree, just want you to make progress here based on what is actually
> required today)
Agreed.
>
>> Platform B: this driver use PCC OpRegion (Currently, this patch does not
>> implement it, but it may be available in the future.)
> Then let us discuss that in the future, don't add unnecessary complexity
> for some future use case today. You can always add it when you introduce
> that feature or support in the future.
Yes. We just need to focus on the current.
If there are any usage problems with PCC OpRegion in the future, we can 
discuss that later.

My original full scheme is as follows:
-->
dev_flags = get_device_flags();  // to know if use PCC OpRegion
if (USE_PCC_OPREGION_B in dev_flags is 0) {
     chan_id = get_pcc_chan_id();
     init_mbox_client();
     pcc_mbox_request_channel(cl, chan_id)
} else {
     /* we need to return unsupport now because of no this feature in 
this driver. */
     do_nothing();
}

void get_some_info(...) {
     if (USE_PCC_OPREGION_B in dev_flags is 0)
         pcc_cmd_send();  // use PCC to communicate with Platform
     else
         acpi_evaluate_object(); // will be used in future.
}

As described in the pseudocode above,
it is necessary to put "dev_flags" in this current driver first in case of
the version driver runs on the platform which just use PCC Opregion.
>
>> Note:
>> This driver selects only one of them (PCC and PCC OpRegion) to communicate
>> with firmware on one platform.
> Let us keep it simple(KISS). The driver works just for PCC not PCC Opregion
> for now.
ok.
>
>> We use one bit in device-flags to know which one this driver will use.
>>
> NACK again just to re-iterate my point if you have not yet accepted that
> fact.
Above is our plan. Do you still think we shouldn't add this device-flags?
please let me know.
>> I'm not sure if you can understand what I mean by saing that.
>> If you're not confused about this now, can you reply to my last email
>> again?😁
>>
> The example you had IIRC is use of System Memory Opregion to demonstrate
> some _DSM. That has nothing to do with PCC Opregion.
Yes, it doesn't matter.
I just want to have a way to get device-flags which contains many 
bits(every bits can be used to as one feature for expanding), rigtht?
>
> Commit 77e2a04745ff ("ACPI: PCC: Implement OperationRegion handler for
> the PCC Type 3 subtype") has the example in the commit message. IIRC,
Your example is very useful to the user.
> you have even fixed couple of bugs in that driver. That is the reason
> why I don't understand how you think this driver and that can or must
Understand you, Sudeep.
At that time, I tested it by a simple demo driver on the platform 
supported type3.

This driver will support multiple platforms.
On some platforms, we can only use PCC with polling way.
And we will add PCC Opregion way for others platforms.
What's more, every platform just use one of them(PCC and PCC Opregion).
> work together. At least I fail to see how ATM(examples please, by that
> I mean ASL snippet for PCC vs PCC Opregion usage to work with this driver)
ok!
For PCC, ASL snippet is little.
I will add ASL snippet when this driver addes PCC Opregion way.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-18  8:24                             ` lihuisong (C)
@ 2023-05-18  8:38                               ` Sudeep Holla
  2023-05-18 12:29                                 ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-18  8:38 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski

On Thu, May 18, 2023 at 04:24:36PM +0800, lihuisong (C) wrote:
> 
> 在 2023/5/17 21:16, Sudeep Holla 写道:
> > On Wed, May 17, 2023 at 07:35:25PM +0800, lihuisong (C) wrote:
> > > 在 2023/5/17 17:30, Sudeep Holla 写道:
> > > > On Wed, May 17, 2023 at 03:16:12PM +0800, lihuisong (C) wrote:
> > > > 
> > > > [...]
> > > > 
> > > > > No. I want to use this flag to make compability between different platforms.
> > > > > This driver only use PCC OpRegion to access to the channel if platform
> > > > > support use PCC OpRegion.
> > > > What do you mean by that ? It is not correct. If there is a PCC Opregion,
> > > > then you need to make it work with drivers/acpi/acpi_pcc.c
> > > > 
> > > > You need to have all the other details in the firmware(ASL). By looking
> > > > at the driver, it has no connection to PCC Opregion IMO unless I am missing
> > > > something.
> > > Driver just needs to call these APIs, such as acpi_evaluate_integer(), if
> > > want to use PCC OpRegion.
> > OK, please provide examples. I am definitely lost as it doesn't match with
> > my understanding of how PCC Opregions are/can be used.
> > 
> > > I know that. I have tested PCC OpRegion before.
> > Cool, examples please.
> > 
> > > You've completely misunderstood what I said.😅
> > > 
> > Hmm, may be but I need examples.
> As you said below, the driver works just for PCC not PCC Opregion for now.
> not sure if we need to discuss how PCC Opregion is used here.

Good let us drop the idea of using PCC Opregion with this driver for now.

> > 
> > > I mean that this driver plans to support both PCC and PCC OpRegion.
> > > For example,
> > > Platform A: this driver use PCC (as the current implementation)
> > Good, then just keep what it needs in the implementation nothing more
> > until you add support for something you have described below(not that
> > I agree, just want you to make progress here based on what is actually
> > required today)
> Agreed.
> > 
> > > Platform B: this driver use PCC OpRegion (Currently, this patch does not
> > > implement it, but it may be available in the future.)
> > Then let us discuss that in the future, don't add unnecessary complexity
> > for some future use case today. You can always add it when you introduce
> > that feature or support in the future.
> Yes. We just need to focus on the current.
> If there are any usage problems with PCC OpRegion in the future, we can
> discuss that later.
>

Agreed.

> My original full scheme is as follows:
> -->
> dev_flags = get_device_flags();  // to know if use PCC OpRegion
> if (USE_PCC_OPREGION_B in dev_flags is 0) {
>     chan_id = get_pcc_chan_id();
>     init_mbox_client();
>     pcc_mbox_request_channel(cl, chan_id)
> } else {
>     /* we need to return unsupport now because of no this feature in this
> driver. */
>     do_nothing();
> }
> 
> void get_some_info(...) {
>     if (USE_PCC_OPREGION_B in dev_flags is 0)
>         pcc_cmd_send();  // use PCC to communicate with Platform
>     else
>         acpi_evaluate_object(); // will be used in future.
> }
> 
> As described in the pseudocode above,
> it is necessary to put "dev_flags" in this current driver first in case of
> the version driver runs on the platform which just use PCC Opregion.

No, you can't randomly define dev_flags just to assist your driver
implementation. If you need it, you need to get the spec updated. We
will not add anything unless that happens.

Note that I don't agree with the flags at all but if you convince and get
them added to spec, I won't object.

> >
> > > Note:
> > > This driver selects only one of them (PCC and PCC OpRegion) to communicate
> > > with firmware on one platform.
> > Let us keep it simple(KISS). The driver works just for PCC not PCC Opregion
> > for now.
> ok.

Good

> > 
> > > We use one bit in device-flags to know which one this driver will use.
> > > 
> > NACK again just to re-iterate my point if you have not yet accepted that
> > fact.
> Above is our plan. Do you still think we shouldn't add this device-flags?
> please let me know.

Correct, no device flags as I see no use for it with your PCC only use case
for now, right ?

> > > I'm not sure if you can understand what I mean by saing that.
> > > If you're not confused about this now, can you reply to my last email
> > > again?😁
> > > 
> > The example you had IIRC is use of System Memory Opregion to demonstrate
> > some _DSM. That has nothing to do with PCC Opregion.
> Yes, it doesn't matter.
> I just want to have a way to get device-flags which contains many bits(every
> bits can be used to as one feature for expanding), rigtht?

Get it through the spec, we don't allow random additions for some
implementations like this.

> > 
> > Commit 77e2a04745ff ("ACPI: PCC: Implement OperationRegion handler for
> > the PCC Type 3 subtype") has the example in the commit message. IIRC,
> Your example is very useful to the user.
> > you have even fixed couple of bugs in that driver. That is the reason
> > why I don't understand how you think this driver and that can or must
> Understand you, Sudeep.
> At that time, I tested it by a simple demo driver on the platform supported
> type3.
>

OK

> This driver will support multiple platforms.
> On some platforms, we can only use PCC with polling way.
> And we will add PCC Opregion way for others platforms.

Again when you do please post the patch with the ASL snippet as I am
very much interested in understanding how you would make that work.

> What's more, every platform just use one of them(PCC and PCC Opregion).

OK

> > work together. At least I fail to see how ATM(examples please, by that
> > I mean ASL snippet for PCC vs PCC Opregion usage to work with this driver)
> ok!
> For PCC, ASL snippet is little.
> I will add ASL snippet when this driver addes PCC Opregion way.


Sounds like a plan to make progress at-least for now.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-18  8:38                               ` Sudeep Holla
@ 2023-05-18 12:29                                 ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-05-18 12:29 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Arnd Bergmann, Bjorn Andersson, Matthias Brugger,
	AngeloGioacchino Del Regno, Shawn Guo, linux-kernel, soc,
	wanghuiqiang, tanxiaofei, liuyonglong, huangdaode, linux-acpi,
	Len Brown, Rafael J. Wysocki, devicetree, Rob Herring,
	Frank Rowand, Krzysztof Kozlowski


在 2023/5/18 16:38, Sudeep Holla 写道:
> On Thu, May 18, 2023 at 04:24:36PM +0800, lihuisong (C) wrote:
>> 在 2023/5/17 21:16, Sudeep Holla 写道:
>>> On Wed, May 17, 2023 at 07:35:25PM +0800, lihuisong (C) wrote:
>>>> 在 2023/5/17 17:30, Sudeep Holla 写道:
>>>>> On Wed, May 17, 2023 at 03:16:12PM +0800, lihuisong (C) wrote:
>>>>>
>>>>> [...]
>>>>>
>>>>>> No. I want to use this flag to make compability between different platforms.
>>>>>> This driver only use PCC OpRegion to access to the channel if platform
>>>>>> support use PCC OpRegion.
>>>>> What do you mean by that ? It is not correct. If there is a PCC Opregion,
>>>>> then you need to make it work with drivers/acpi/acpi_pcc.c
>>>>>
>>>>> You need to have all the other details in the firmware(ASL). By looking
>>>>> at the driver, it has no connection to PCC Opregion IMO unless I am missing
>>>>> something.
>>>> Driver just needs to call these APIs, such as acpi_evaluate_integer(), if
>>>> want to use PCC OpRegion.
>>> OK, please provide examples. I am definitely lost as it doesn't match with
>>> my understanding of how PCC Opregions are/can be used.
>>>
>>>> I know that. I have tested PCC OpRegion before.
>>> Cool, examples please.
>>>
>>>> You've completely misunderstood what I said.😅
>>>>
>>> Hmm, may be but I need examples.
>> As you said below, the driver works just for PCC not PCC Opregion for now.
>> not sure if we need to discuss how PCC Opregion is used here.
> Good let us drop the idea of using PCC Opregion with this driver for now.
>
>>>> I mean that this driver plans to support both PCC and PCC OpRegion.
>>>> For example,
>>>> Platform A: this driver use PCC (as the current implementation)
>>> Good, then just keep what it needs in the implementation nothing more
>>> until you add support for something you have described below(not that
>>> I agree, just want you to make progress here based on what is actually
>>> required today)
>> Agreed.
>>>> Platform B: this driver use PCC OpRegion (Currently, this patch does not
>>>> implement it, but it may be available in the future.)
>>> Then let us discuss that in the future, don't add unnecessary complexity
>>> for some future use case today. You can always add it when you introduce
>>> that feature or support in the future.
>> Yes. We just need to focus on the current.
>> If there are any usage problems with PCC OpRegion in the future, we can
>> discuss that later.
>>
> Agreed.
>
>> My original full scheme is as follows:
>> -->
>> dev_flags = get_device_flags();  // to know if use PCC OpRegion
>> if (USE_PCC_OPREGION_B in dev_flags is 0) {
>>      chan_id = get_pcc_chan_id();
>>      init_mbox_client();
>>      pcc_mbox_request_channel(cl, chan_id)
>> } else {
>>      /* we need to return unsupport now because of no this feature in this
>> driver. */
>>      do_nothing();
>> }
>>
>> void get_some_info(...) {
>>      if (USE_PCC_OPREGION_B in dev_flags is 0)
>>          pcc_cmd_send();  // use PCC to communicate with Platform
>>      else
>>          acpi_evaluate_object(); // will be used in future.
>> }
>>
>> As described in the pseudocode above,
>> it is necessary to put "dev_flags" in this current driver first in case of
>> the version driver runs on the platform which just use PCC Opregion.
> No, you can't randomly define dev_flags just to assist your driver
> implementation. If you need it, you need to get the spec updated. We
> will not add anything unless that happens.
>
> Note that I don't agree with the flags at all but if you convince and get
> them added to spec, I won't object.
Ok,let us drop it.
>>>> Note:
>>>> This driver selects only one of them (PCC and PCC OpRegion) to communicate
>>>> with firmware on one platform.
>>> Let us keep it simple(KISS). The driver works just for PCC not PCC Opregion
>>> for now.
>> ok.
> Good
>
>>>> We use one bit in device-flags to know which one this driver will use.
>>>>
>>> NACK again just to re-iterate my point if you have not yet accepted that
>>> fact.
>> Above is our plan. Do you still think we shouldn't add this device-flags?
>> please let me know.
> Correct, no device flags as I see no use for it with your PCC only use case
> for now, right ?
Yes, it can still work well.
As for PCC Opregion way on other platform, I think of other way.
>
>>>> I'm not sure if you can understand what I mean by saing that.
>>>> If you're not confused about this now, can you reply to my last email
>>>> again?😁
>>>>
>>> The example you had IIRC is use of System Memory Opregion to demonstrate
>>> some _DSM. That has nothing to do with PCC Opregion.
>> Yes, it doesn't matter.
>> I just want to have a way to get device-flags which contains many bits(every
>> bits can be used to as one feature for expanding), rigtht?
> Get it through the spec, we don't allow random additions for some
> implementations like this.
Get it.
>>> Commit 77e2a04745ff ("ACPI: PCC: Implement OperationRegion handler for
>>> the PCC Type 3 subtype") has the example in the commit message. IIRC,
>> Your example is very useful to the user.
>>> you have even fixed couple of bugs in that driver. That is the reason
>>> why I don't understand how you think this driver and that can or must
>> Understand you, Sudeep.
>> At that time, I tested it by a simple demo driver on the platform supported
>> type3.
>>
> OK
>
>> This driver will support multiple platforms.
>> On some platforms, we can only use PCC with polling way.
>> And we will add PCC Opregion way for others platforms.
> Again when you do please post the patch with the ASL snippet as I am
> very much interested in understanding how you would make that work.
All right.
>
>> What's more, every platform just use one of them(PCC and PCC Opregion).
> OK
>
>>> work together. At least I fail to see how ATM(examples please, by that
>>> I mean ASL snippet for PCC vs PCC Opregion usage to work with this driver)
>> ok!
>> For PCC, ASL snippet is little.
>> I will add ASL snippet when this driver addes PCC Opregion way.
>
> Sounds like a plan to make progress at-least for now.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v2 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-04-24  8:09 ` Arnd Bergmann
  2023-04-24  8:42 ` Krzysztof Kozlowski
@ 2023-05-22  7:22 ` Huisong Li
  2023-05-22  7:22   ` [PATCH v2 1/2] " Huisong Li
  2023-05-22  7:22   ` [PATCH v2 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 79+ messages in thread
From: Huisong Li @ 2023-05-22  7:22 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (2):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   76 +
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   19 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1287 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
 8 files changed, 1589 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-22  7:22 ` [PATCH v2 0/2] " Huisong Li
@ 2023-05-22  7:22   ` Huisong Li
  2023-05-23  9:39     ` Sudeep Holla
  2023-05-22  7:22   ` [PATCH v2 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  1 sibling, 1 reply; 79+ messages in thread
From: Huisong Li @ 2023-05-22  7:22 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, lihuisong

The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
for ensuring cache coherent on HiSilicon SoC. The performance of
the application may be affected if some hccs ports are in non-full
lane status, have a large number of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   19 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1287 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
 7 files changed, 1512 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eddbc48c61e9..fe0e796e8445 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9399,6 +9399,12 @@ S:	Maintained
 W:	http://www.hisilicon.com
 F:	drivers/spi/spi-hisi-sfc-v3xx.c
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HMM - Heterogeneous Memory Management
 M:	Jérôme Glisse <jglisse@redhat.com>
 L:	linux-mm@kvack.org
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 3b0f9fb3b5c8..531f46f3ad98 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..87a1f15cbedb
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
+	  for ensuring cache coherent on HiSilicon SoC. The performance of
+	  the application may be affected if some hccs ports are in non-full
+	  lane status, have a large number of CRC errors and so on.
+
+	  Say M here if you want to include support for querying the health
+	  status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..20a506a04bb7
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
+ * ensuring cache coherent on HiSilicon SoC.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve info as belows each port:
+ *    - port type
+ *    - lane mode
+ *    - using status
+ *    - current lane mode
+ *    - link state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve info as belows all ports on die or chip:
+ *    - if all used ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->is_used = attrs[i].is_used;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr =
+			__ATTR(type, 0444, type_show, NULL);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr =
+			__ATTR(lane_mode, 0444, lane_mode_show, NULL);
+
+static ssize_t using_status_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->is_used);
+}
+
+static struct kobj_attribute using_status_attr =
+			__ATTR(using_status, 0444, using_status_show, NULL);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr =
+			__ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr =
+			__ATTR(link_fsm, 0444, link_fsm_show, NULL);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr =
+			__ATTR(lane_mask, 0444, lane_mask_show, NULL);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr =
+			__ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&using_status_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1", },
+};
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..d411178f8026
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool is_used; /* if the link is used. */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 is_used : 1; /* if the link is used. */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v2 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-05-22  7:22 ` [PATCH v2 0/2] " Huisong Li
  2023-05-22  7:22   ` [PATCH v2 1/2] " Huisong Li
@ 2023-05-22  7:22   ` Huisong Li
  1 sibling, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-05-22  7:22 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 76 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 77 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..641dd7a0e428
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,76 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.4
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all used ports on this chip are linked
+				       (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.4
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all used ports on this die are linked
+				       (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/using_status
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.4
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+
+		The HCCS port have the following attributes:
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		using_status: (RO) indicate if the port is used (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
+		Note: type, lane_mode and using_status are fixed attributes on
+		      running platform.
diff --git a/MAINTAINERS b/MAINTAINERS
index fe0e796e8445..30180136b701 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9402,6 +9402,7 @@ F:	drivers/spi/spi-hisi-sfc-v3xx.c
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-22  7:22   ` [PATCH v2 1/2] " Huisong Li
@ 2023-05-23  9:39     ` Sudeep Holla
  2023-05-23 11:57       ` lihuisong (C)
  2023-05-25  2:41       ` lihuisong (C)
  0 siblings, 2 replies; 79+ messages in thread
From: Sudeep Holla @ 2023-05-23  9:39 UTC (permalink / raw)
  To: Huisong Li
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	Sudeep Holla, arnd, krzk, linux-kernel, soc, wanghuiqiang,
	tanxiaofei, liuyonglong

On Mon, May 22, 2023 at 03:22:10PM +0800, Huisong Li wrote:
> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> for ensuring cache coherent on HiSilicon SoC. The performance of
> the application may be affected if some hccs ports are in non-full
> lane status, have a large number of CRC errors and so on.
> 
> This driver provides the query interface of the health status and
> port information of HCCS on Kunpeng SoC.
> 
> Signed-off-by: Huisong Li <lihuisong@huawei.com>
> ---
>  MAINTAINERS                          |    6 +
>  drivers/soc/Kconfig                  |    1 +
>  drivers/soc/Makefile                 |    1 +
>  drivers/soc/hisilicon/Kconfig        |   19 +
>  drivers/soc/hisilicon/Makefile       |    2 +
>  drivers/soc/hisilicon/kunpeng_hccs.c | 1287 ++++++++++++++++++++++++++
>  drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>  7 files changed, 1512 insertions(+)
>  create mode 100644 drivers/soc/hisilicon/Kconfig
>  create mode 100644 drivers/soc/hisilicon/Makefile
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index eddbc48c61e9..fe0e796e8445 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9399,6 +9399,12 @@ S:	Maintained
>  W:	http://www.hisilicon.com
>  F:	drivers/spi/spi-hisi-sfc-v3xx.c
>  
> +HISILICON KUNPENG SOC HCCS DRIVER

s/HCCS/HCCS INFO or QUERY/ ?

> +M:	Huisong Li <lihuisong@huawei.com>
> +S:	Maintained
> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
> +
>  HMM - Heterogeneous Memory Management
>  M:	Jérôme Glisse <jglisse@redhat.com>
>  L:	linux-mm@kvack.org
> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
> index 4e176280113a..d21e75d69294 100644
> --- a/drivers/soc/Kconfig
> +++ b/drivers/soc/Kconfig
> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>  source "drivers/soc/canaan/Kconfig"
>  source "drivers/soc/fsl/Kconfig"
>  source "drivers/soc/fujitsu/Kconfig"
> +source "drivers/soc/hisilicon/Kconfig"
>  source "drivers/soc/imx/Kconfig"
>  source "drivers/soc/ixp4xx/Kconfig"
>  source "drivers/soc/litex/Kconfig"
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 3b0f9fb3b5c8..531f46f3ad98 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>  obj-y				+= fsl/
>  obj-y				+= fujitsu/
>  obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
> +obj-y				+= hisilicon/
>  obj-y				+= imx/
>  obj-y				+= ixp4xx/
>  obj-$(CONFIG_SOC_XWAY)		+= lantiq/
> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
> new file mode 100644
> index 000000000000..87a1f15cbedb
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Kconfig
> @@ -0,0 +1,19 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Hisilicon SoC drivers"
> +	depends on ARCH_HISI || COMPILE_TEST
> +
> +config KUNPENG_HCCS

Ditto, add INFO or QUERY to the name as HCCS alone suggests it is some
driver to manage the above bus protocol which is not correct.

> +	tristate "HCCS driver on Kunpeng SoC"
> +	depends on ACPI
> +	depends on ARM64 || COMPILE_TEST
> +	help
> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
> +	  the application may be affected if some hccs ports are in non-full
> +	  lane status, have a large number of CRC errors and so on.
> +
> +	  Say M here if you want to include support for querying the health
> +	  status and port information of HCCS on Kunpeng SoC.
> +
> +endmenu
> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
> new file mode 100644
> index 000000000000..226e747e70d6
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
> new file mode 100644
> index 000000000000..20a506a04bb7
> --- /dev/null
> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
> @@ -0,0 +1,1287 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
> + * ensuring cache coherent on HiSilicon SoC.
> + *
> + * Copyright (c) 2023 Hisilicon Limited.
> + * Author: Huisong Li <lihuisong@huawei.com>
> + *
> + * HCCS driver for Kunpeng SoC provides the following features:
> + * - Retrieve info as belows each port:

Can we have something like:
"Retrieve the following information about each port:"

> + *    - port type
> + *    - lane mode
> + *    - using status

perhaps just status, "using status" doesn't sound correct to me.

> + *    - current lane mode
> + *    - link state machine
> + *    - lane mask
> + *    - CRC error count
> + *
> + * - Retrieve info as belows all ports on die or chip:

Similarly:
"Retrieve the following information about all the ports on the chip or the die:"

> + *    - if all used ports are in linked
> + *    - if all linked ports are in full lane
> + *    - CRC error count sum
> + */
> +#include <linux/sysfs.h>
> +#include <linux/acpi.h>
> +#include <linux/io.h>
> +#include <linux/kobject.h>
> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>
> +#include <acpi/pcc.h>
> +
> +#include "kunpeng_hccs.h"
> +
> +/* PCC defines */
> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)

I am not really sure if we keep duplicating this. I will try to cook up
a patch consolidating these.

> +
> +/*
> + * Arbitrary retries in case the remote processor is slow to respond
> + * to PCC commands
> + */
> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
> +
> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_port_info, kobj);
> +}
> +
> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_die_info, kobj);
> +}
> +
> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_chip_info, kobj);
> +}
> +
> +struct hccs_register_ctx {
> +	struct device *dev;
> +	u8 chan_id;
> +	int err;
> +};
> +
> +static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
> +					void *context)
> +{
> +	struct acpi_resource_generic_register *reg;
> +	struct hccs_register_ctx *ctx = context;
> +
> +	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
> +		return AE_OK;
> +
> +	reg = &ares->data.generic_reg;
> +	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
> +		dev_err(ctx->dev, "Bad register resource.\n");
> +		ctx->err = -EINVAL;
> +		return AE_ERROR;
> +	}
> +	ctx->chan_id = reg->access_size;
> +
> +	return AE_OK;
> +}
> +
> +static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
> +{
> +	acpi_handle handle = ACPI_HANDLE(hdev->dev);
> +	struct hccs_register_ctx ctx = {0};
> +	acpi_status status;
> +
> +	if (!acpi_has_method(handle, METHOD_NAME__CRS))
> +		return -ENODEV;
> +
> +	ctx.dev = hdev->dev;
> +	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
> +				     hccs_get_register_cb, &ctx);
> +	if (ACPI_FAILURE(status))
> +		return ctx.err;
> +	hdev->chan_id = ctx.chan_id;
> +
> +	return 0;
> +}
> +
> +static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
> +{
> +	if (ret < 0)
> +		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
> +			 *(u8 *)msg, ret);
> +	else
> +		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
> +			 *(u8 *)msg, ret);
> +}
> +
> +static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +
> +	if (cl_info->pcc_comm_addr)
> +		iounmap(cl_info->pcc_comm_addr);
> +	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
> +}
> +
> +static int hccs_register_pcc_channel(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct mbox_client *cl = &cl_info->client;
> +	struct pcc_mbox_chan *pcc_chan;
> +	struct device *dev = hdev->dev;
> +	int rc;
> +
> +	cl->dev = dev;
> +	cl->tx_block = false;
> +	cl->knows_txdone = true;
> +	cl->tx_done = hccs_chan_tx_done;
> +	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
> +	if (IS_ERR(pcc_chan)) {
> +		dev_err(dev, "PPC channel request failed.\n");
> +		rc = -ENODEV;
> +		goto out;
> +	}
> +	cl_info->pcc_chan = pcc_chan;
> +	cl_info->mbox_chan = pcc_chan->mchan;
> +
> +	/*
> +	 * pcc_chan->latency is just a nominal value. In reality the remote
> +	 * processor could be much slower to reply. So add an arbitrary amount
> +	 * of wait on top of nominal.
> +	 */
> +	cl_info->deadline_us =
> +			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
> +	if (cl_info->mbox_chan->mbox->txdone_irq) {
> +		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
> +		rc = -EINVAL;
> +		goto err_mbx_channel_free;
> +	}
> +
> +	if (pcc_chan->shmem_base_addr) {
> +		cl_info->pcc_comm_addr = (void __force *)ioremap(

I would prefer to use acpi_os_ioremap as it has addition checks to ensure
it is not in any EFI mappings.

> +			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
> +		if (!cl_info->pcc_comm_addr) {
> +			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
> +				hdev->chan_id);
> +			rc = -ENOMEM;
> +			goto err_mbx_channel_free;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_mbx_channel_free:
> +	pcc_mbox_free_channel(cl_info->pcc_chan);
> +out:
> +	return rc;
> +}
> +
> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
> +	u16 status;
> +	int ret;
> +
> +	/*
> +	 * Poll PCC status register every 3us(delay_us) for maximum of
> +	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
> +	 */
> +	ret = readw_poll_timeout(&comm_base->status, status,
> +				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
> +				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
> +				 cl_info->deadline_us);
> +	if (unlikely(ret))
> +		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
> +
> +	return ret;
> +}
> +
> +static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
> +			     struct hccs_desc *desc)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
> +	void *comm_space = (void *)(comm_base + 1);
> +	struct hccs_fw_inner_head *fw_inner_head;
> +	struct acpi_pcct_shared_memory tmp = {0};
> +	u16 comm_space_size;
> +	int ret;
> +
> +	/* Write signature for this subspace */
> +	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;

Why do we need to do this every single time ? This is never changed as
it is fixed for a channel, so wondering if it can be done once in probe or
so ?

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-23  9:39     ` Sudeep Holla
@ 2023-05-23 11:57       ` lihuisong (C)
  2023-05-23 13:41         ` Sudeep Holla
  2023-05-25  2:41       ` lihuisong (C)
  1 sibling, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-23 11:57 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong


在 2023/5/23 17:39, Sudeep Holla 写道:
> On Mon, May 22, 2023 at 03:22:10PM +0800, Huisong Li wrote:
>> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> for ensuring cache coherent on HiSilicon SoC. The performance of
>> the application may be affected if some hccs ports are in non-full
>> lane status, have a large number of CRC errors and so on.
>>
>> This driver provides the query interface of the health status and
>> port information of HCCS on Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>> ---
>>   MAINTAINERS                          |    6 +
>>   drivers/soc/Kconfig                  |    1 +
>>   drivers/soc/Makefile                 |    1 +
>>   drivers/soc/hisilicon/Kconfig        |   19 +
>>   drivers/soc/hisilicon/Makefile       |    2 +
>>   drivers/soc/hisilicon/kunpeng_hccs.c | 1287 ++++++++++++++++++++++++++
>>   drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>>   7 files changed, 1512 insertions(+)
>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index eddbc48c61e9..fe0e796e8445 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9399,6 +9399,12 @@ S:	Maintained
>>   W:	http://www.hisilicon.com
>>   F:	drivers/spi/spi-hisi-sfc-v3xx.c
>>   
>> +HISILICON KUNPENG SOC HCCS DRIVER
> s/HCCS/HCCS INFO or QUERY/ ?
 From the current features, what you suggest is suitable.
But this driver will support to the setting feature.
>
>> +M:	Huisong Li <lihuisong@huawei.com>
>> +S:	Maintained
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
>> +
>>   HMM - Heterogeneous Memory Management
>>   M:	Jérôme Glisse <jglisse@redhat.com>
>>   L:	linux-mm@kvack.org
>> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
>> index 4e176280113a..d21e75d69294 100644
>> --- a/drivers/soc/Kconfig
>> +++ b/drivers/soc/Kconfig
>> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>>   source "drivers/soc/canaan/Kconfig"
>>   source "drivers/soc/fsl/Kconfig"
>>   source "drivers/soc/fujitsu/Kconfig"
>> +source "drivers/soc/hisilicon/Kconfig"
>>   source "drivers/soc/imx/Kconfig"
>>   source "drivers/soc/ixp4xx/Kconfig"
>>   source "drivers/soc/litex/Kconfig"
>> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
>> index 3b0f9fb3b5c8..531f46f3ad98 100644
>> --- a/drivers/soc/Makefile
>> +++ b/drivers/soc/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>>   obj-y				+= fsl/
>>   obj-y				+= fujitsu/
>>   obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
>> +obj-y				+= hisilicon/
>>   obj-y				+= imx/
>>   obj-y				+= ixp4xx/
>>   obj-$(CONFIG_SOC_XWAY)		+= lantiq/
>> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..87a1f15cbedb
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,19 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +
>> +config KUNPENG_HCCS
> Ditto, add INFO or QUERY to the name as HCCS alone suggests it is some
> driver to manage the above bus protocol which is not correct.
Ditto, the setting feature will be added soon.
>
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ACPI
>> +	depends on ARM64 || COMPILE_TEST
>> +	help
>> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
>> +	  the application may be affected if some hccs ports are in non-full
>> +	  lane status, have a large number of CRC errors and so on.
>> +
>> +	  Say M here if you want to include support for querying the health
>> +	  status and port information of HCCS on Kunpeng SoC.
>> +
>> +endmenu
>> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
>> new file mode 100644
>> index 000000000000..226e747e70d6
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Makefile
>> @@ -0,0 +1,2 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
>> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
>> new file mode 100644
>> index 000000000000..20a506a04bb7
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
>> @@ -0,0 +1,1287 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
>> + * ensuring cache coherent on HiSilicon SoC.
>> + *
>> + * Copyright (c) 2023 Hisilicon Limited.
>> + * Author: Huisong Li <lihuisong@huawei.com>
>> + *
>> + * HCCS driver for Kunpeng SoC provides the following features:
>> + * - Retrieve info as belows each port:
> Can we have something like:
> "Retrieve the following information about each port:"
Ok, it's better.
>
>> + *    - port type
>> + *    - lane mode
>> + *    - using status
> perhaps just status, "using status" doesn't sound correct to me.
Ack
>
>> + *    - current lane mode
>> + *    - link state machine
>> + *    - lane mask
>> + *    - CRC error count
>> + *
>> + * - Retrieve info as belows all ports on die or chip:
> Similarly:
> "Retrieve the following information about all the ports on the chip or the die:"
Ack
>
>> + *    - if all used ports are in linked
>> + *    - if all linked ports are in full lane
>> + *    - CRC error count sum
>> + */
>> +#include <linux/sysfs.h>
>> +#include <linux/acpi.h>
>> +#include <linux/io.h>
>> +#include <linux/kobject.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/platform_device.h>
>> +#include <acpi/pcc.h>
>> +
>> +#include "kunpeng_hccs.h"
>> +
>> +/* PCC defines */
>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
> I am not really sure if we keep duplicating this. I will try to cook up
> a patch consolidating these.
Yes, these info is better placed in a global header.
Do I need to do this in this series?
Or do not modify it, and then process all related code in your patch.
>
>> +
>> +/*
>> + * Arbitrary retries in case the remote processor is slow to respond
>> + * to PCC commands
>> + */
>> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
>> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
>> +
>> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_port_info, kobj);
>> +}
>> +
>> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_die_info, kobj);
>> +}
>> +
>> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_chip_info, kobj);
>> +}
>> +
>> +struct hccs_register_ctx {
>> +	struct device *dev;
>> +	u8 chan_id;
>> +	int err;
>> +};
>> +
>> +static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
>> +					void *context)
>> +{
>> +	struct acpi_resource_generic_register *reg;
>> +	struct hccs_register_ctx *ctx = context;
>> +
>> +	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
>> +		return AE_OK;
>> +
>> +	reg = &ares->data.generic_reg;
>> +	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
>> +		dev_err(ctx->dev, "Bad register resource.\n");
>> +		ctx->err = -EINVAL;
>> +		return AE_ERROR;
>> +	}
>> +	ctx->chan_id = reg->access_size;
>> +
>> +	return AE_OK;
>> +}
>> +
>> +static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
>> +{
>> +	acpi_handle handle = ACPI_HANDLE(hdev->dev);
>> +	struct hccs_register_ctx ctx = {0};
>> +	acpi_status status;
>> +
>> +	if (!acpi_has_method(handle, METHOD_NAME__CRS))
>> +		return -ENODEV;
>> +
>> +	ctx.dev = hdev->dev;
>> +	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
>> +				     hccs_get_register_cb, &ctx);
>> +	if (ACPI_FAILURE(status))
>> +		return ctx.err;
>> +	hdev->chan_id = ctx.chan_id;
>> +
>> +	return 0;
>> +}
>> +
>> +static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
>> +{
>> +	if (ret < 0)
>> +		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
>> +			 *(u8 *)msg, ret);
>> +	else
>> +		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
>> +			 *(u8 *)msg, ret);
>> +}
>> +
>> +static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +
>> +	if (cl_info->pcc_comm_addr)
>> +		iounmap(cl_info->pcc_comm_addr);
>> +	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
>> +}
>> +
>> +static int hccs_register_pcc_channel(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct mbox_client *cl = &cl_info->client;
>> +	struct pcc_mbox_chan *pcc_chan;
>> +	struct device *dev = hdev->dev;
>> +	int rc;
>> +
>> +	cl->dev = dev;
>> +	cl->tx_block = false;
>> +	cl->knows_txdone = true;
>> +	cl->tx_done = hccs_chan_tx_done;
>> +	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
>> +	if (IS_ERR(pcc_chan)) {
>> +		dev_err(dev, "PPC channel request failed.\n");
>> +		rc = -ENODEV;
>> +		goto out;
>> +	}
>> +	cl_info->pcc_chan = pcc_chan;
>> +	cl_info->mbox_chan = pcc_chan->mchan;
>> +
>> +	/*
>> +	 * pcc_chan->latency is just a nominal value. In reality the remote
>> +	 * processor could be much slower to reply. So add an arbitrary amount
>> +	 * of wait on top of nominal.
>> +	 */
>> +	cl_info->deadline_us =
>> +			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
>> +	if (cl_info->mbox_chan->mbox->txdone_irq) {
>> +		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
>> +		rc = -EINVAL;
>> +		goto err_mbx_channel_free;
>> +	}
>> +
>> +	if (pcc_chan->shmem_base_addr) {
>> +		cl_info->pcc_comm_addr = (void __force *)ioremap(
> I would prefer to use acpi_os_ioremap as it has addition checks to ensure
> it is not in any EFI mappings.
Agreed. will fix it.
>
>> +			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
>> +		if (!cl_info->pcc_comm_addr) {
>> +			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
>> +				hdev->chan_id);
>> +			rc = -ENOMEM;
>> +			goto err_mbx_channel_free;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +err_mbx_channel_free:
>> +	pcc_mbox_free_channel(cl_info->pcc_chan);
>> +out:
>> +	return rc;
>> +}
>> +
>> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
>> +	u16 status;
>> +	int ret;
>> +
>> +	/*
>> +	 * Poll PCC status register every 3us(delay_us) for maximum of
>> +	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
>> +	 */
>> +	ret = readw_poll_timeout(&comm_base->status, status,
>> +				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
>> +				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
>> +				 cl_info->deadline_us);
>> +	if (unlikely(ret))
>> +		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
>> +			     struct hccs_desc *desc)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
>> +	void *comm_space = (void *)(comm_base + 1);
>> +	struct hccs_fw_inner_head *fw_inner_head;
>> +	struct acpi_pcct_shared_memory tmp = {0};
>> +	u16 comm_space_size;
>> +	int ret;
>> +
>> +	/* Write signature for this subspace */
>> +	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
> Why do we need to do this every single time ? This is never changed as
> it is fixed for a channel, so wondering if it can be done once in probe or
> so ?
 From the driver side, this will be ok.
But this initialization from driver will be cleared after firmware reseting.
And driver doesn't know about it.
IMO, there's no need to make driver and firmware complicated because of 
an assignment statement.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-23 11:57       ` lihuisong (C)
@ 2023-05-23 13:41         ` Sudeep Holla
  2023-05-24  9:36           ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-23 13:41 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong

On Tue, May 23, 2023 at 07:57:42PM +0800, lihuisong (C) wrote:
> 
> 在 2023/5/23 17:39, Sudeep Holla 写道:
> > On Mon, May 22, 2023 at 03:22:10PM +0800, Huisong Li wrote:
> > > The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> > > for ensuring cache coherent on HiSilicon SoC. The performance of
> > > the application may be affected if some hccs ports are in non-full
> > > lane status, have a large number of CRC errors and so on.
> > > 
> > > This driver provides the query interface of the health status and
> > > port information of HCCS on Kunpeng SoC.
> > > 
> > > Signed-off-by: Huisong Li <lihuisong@huawei.com>
> > > ---
> > >   MAINTAINERS                          |    6 +
> > >   drivers/soc/Kconfig                  |    1 +
> > >   drivers/soc/Makefile                 |    1 +
> > >   drivers/soc/hisilicon/Kconfig        |   19 +
> > >   drivers/soc/hisilicon/Makefile       |    2 +
> > >   drivers/soc/hisilicon/kunpeng_hccs.c | 1287 ++++++++++++++++++++++++++
> > >   drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
> > >   7 files changed, 1512 insertions(+)
> > >   create mode 100644 drivers/soc/hisilicon/Kconfig
> > >   create mode 100644 drivers/soc/hisilicon/Makefile
> > >   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
> > >   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
> > > 
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index eddbc48c61e9..fe0e796e8445 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -9399,6 +9399,12 @@ S:	Maintained
> > >   W:	http://www.hisilicon.com
> > >   F:	drivers/spi/spi-hisi-sfc-v3xx.c
> > > +HISILICON KUNPENG SOC HCCS DRIVER
> > s/HCCS/HCCS INFO or QUERY/ ?
> From the current features, what you suggest is suitable.
> But this driver will support to the setting feature.

Ah OK, I wasn't aware of that, in that case it is fine to keep it as is
and add comment about future feature expansions so that the name makes
more sense them. I assumed it is more a query interface.

I also assume the settings with just control performance and is fully
safe and stability of the system is taken care.

[...]

> > > +
> > > +/* PCC defines */
> > > +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
> > > +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
> > I am not really sure if we keep duplicating this. I will try to cook up
> > a patch consolidating these.
> Yes, these info is better placed in a global header.
> Do I need to do this in this series?
> Or do not modify it, and then process all related code in your patch.

Fair enough, we can do that later. I don't want to block this for that reason.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-23 13:41         ` Sudeep Holla
@ 2023-05-24  9:36           ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-05-24  9:36 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong


在 2023/5/23 21:41, Sudeep Holla 写道:
> On Tue, May 23, 2023 at 07:57:42PM +0800, lihuisong (C) wrote:
>> 在 2023/5/23 17:39, Sudeep Holla 写道:
>>> On Mon, May 22, 2023 at 03:22:10PM +0800, Huisong Li wrote:
>>>> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>>>> for ensuring cache coherent on HiSilicon SoC. The performance of
>>>> the application may be affected if some hccs ports are in non-full
>>>> lane status, have a large number of CRC errors and so on.
>>>>
>>>> This driver provides the query interface of the health status and
>>>> port information of HCCS on Kunpeng SoC.
>>>>
>>>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>>>> ---
>>>>    MAINTAINERS                          |    6 +
>>>>    drivers/soc/Kconfig                  |    1 +
>>>>    drivers/soc/Makefile                 |    1 +
>>>>    drivers/soc/hisilicon/Kconfig        |   19 +
>>>>    drivers/soc/hisilicon/Makefile       |    2 +
>>>>    drivers/soc/hisilicon/kunpeng_hccs.c | 1287 ++++++++++++++++++++++++++
>>>>    drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>>>>    7 files changed, 1512 insertions(+)
>>>>    create mode 100644 drivers/soc/hisilicon/Kconfig
>>>>    create mode 100644 drivers/soc/hisilicon/Makefile
>>>>    create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>>>>    create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index eddbc48c61e9..fe0e796e8445 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -9399,6 +9399,12 @@ S:	Maintained
>>>>    W:	http://www.hisilicon.com
>>>>    F:	drivers/spi/spi-hisi-sfc-v3xx.c
>>>> +HISILICON KUNPENG SOC HCCS DRIVER
>>> s/HCCS/HCCS INFO or QUERY/ ?
>>  From the current features, what you suggest is suitable.
>> But this driver will support to the setting feature.
> Ah OK, I wasn't aware of that, in that case it is fine to keep it as is
> and add comment about future feature expansions so that the name makes
It's not suitable to add it in advance.
> more sense them. I assumed it is more a query interface.
Yes, most of them are query interfaces.
>
> I also assume the settings with just control performance and is fully
> safe and stability of the system is taken care.
Yes, it will has an impact on performance and power consumption, but not 
functionality.
>
> [...]
>
>>>> +
>>>> +/* PCC defines */
>>>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>>>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
>>> I am not really sure if we keep duplicating this. I will try to cook up
>>> a patch consolidating these.
>> Yes, these info is better placed in a global header.
>> Do I need to do this in this series?
>> Or do not modify it, and then process all related code in your patch.
> Fair enough, we can do that later. I don't want to block this for that reason.
ok, thanks. will send v3 ASAP.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-23  9:39     ` Sudeep Holla
  2023-05-23 11:57       ` lihuisong (C)
@ 2023-05-25  2:41       ` lihuisong (C)
  2023-05-25  7:35         ` Sudeep Holla
  1 sibling, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-25  2:41 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong


在 2023/5/23 17:39, Sudeep Holla 写道:
> On Mon, May 22, 2023 at 03:22:10PM +0800, Huisong Li wrote:
>> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> for ensuring cache coherent on HiSilicon SoC. The performance of
>> the application may be affected if some hccs ports are in non-full
>> lane status, have a large number of CRC errors and so on.
>>
>> This driver provides the query interface of the health status and
>> port information of HCCS on Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>> ---
>>   MAINTAINERS                          |    6 +
>>   drivers/soc/Kconfig                  |    1 +
>>   drivers/soc/Makefile                 |    1 +
>>   drivers/soc/hisilicon/Kconfig        |   19 +
>>   drivers/soc/hisilicon/Makefile       |    2 +
>>   drivers/soc/hisilicon/kunpeng_hccs.c | 1287 ++++++++++++++++++++++++++
>>   drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>>   7 files changed, 1512 insertions(+)
>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index eddbc48c61e9..fe0e796e8445 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9399,6 +9399,12 @@ S:	Maintained
>>   W:	http://www.hisilicon.com
>>   F:	drivers/spi/spi-hisi-sfc-v3xx.c
>>   
>> +HISILICON KUNPENG SOC HCCS DRIVER
> s/HCCS/HCCS INFO or QUERY/ ?
>
>> +M:	Huisong Li <lihuisong@huawei.com>
>> +S:	Maintained
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
>> +
>>   HMM - Heterogeneous Memory Management
>>   M:	Jérôme Glisse <jglisse@redhat.com>
>>   L:	linux-mm@kvack.org
>> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
>> index 4e176280113a..d21e75d69294 100644
>> --- a/drivers/soc/Kconfig
>> +++ b/drivers/soc/Kconfig
>> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>>   source "drivers/soc/canaan/Kconfig"
>>   source "drivers/soc/fsl/Kconfig"
>>   source "drivers/soc/fujitsu/Kconfig"
>> +source "drivers/soc/hisilicon/Kconfig"
>>   source "drivers/soc/imx/Kconfig"
>>   source "drivers/soc/ixp4xx/Kconfig"
>>   source "drivers/soc/litex/Kconfig"
>> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
>> index 3b0f9fb3b5c8..531f46f3ad98 100644
>> --- a/drivers/soc/Makefile
>> +++ b/drivers/soc/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>>   obj-y				+= fsl/
>>   obj-y				+= fujitsu/
>>   obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
>> +obj-y				+= hisilicon/
>>   obj-y				+= imx/
>>   obj-y				+= ixp4xx/
>>   obj-$(CONFIG_SOC_XWAY)		+= lantiq/
>> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..87a1f15cbedb
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,19 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +
>> +config KUNPENG_HCCS
> Ditto, add INFO or QUERY to the name as HCCS alone suggests it is some
> driver to manage the above bus protocol which is not correct.
>
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ACPI
>> +	depends on ARM64 || COMPILE_TEST
>> +	help
>> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
>> +	  the application may be affected if some hccs ports are in non-full
>> +	  lane status, have a large number of CRC errors and so on.
>> +
>> +	  Say M here if you want to include support for querying the health
>> +	  status and port information of HCCS on Kunpeng SoC.
>> +
>> +endmenu
>> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
>> new file mode 100644
>> index 000000000000..226e747e70d6
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Makefile
>> @@ -0,0 +1,2 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
>> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
>> new file mode 100644
>> index 000000000000..20a506a04bb7
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
>> @@ -0,0 +1,1287 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
>> + * ensuring cache coherent on HiSilicon SoC.
>> + *
>> + * Copyright (c) 2023 Hisilicon Limited.
>> + * Author: Huisong Li <lihuisong@huawei.com>
>> + *
>> + * HCCS driver for Kunpeng SoC provides the following features:
>> + * - Retrieve info as belows each port:
> [snip]
>
>> + *    - port type
>> + *    - lane mode
>> + *    - using status
> perhaps just status, "using status" doesn't sound correct to me.
>
>
Hi Sudeep,

Here, the interface is used to determine whether a port is in use or 
enabled.
If we just use 'status', it cannot inidicates its own meaning by name.
What do you think?

/Huisong

[snip]


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-25  2:41       ` lihuisong (C)
@ 2023-05-25  7:35         ` Sudeep Holla
  2023-05-25  8:12           ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-25  7:35 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	Sudeep Holla, liuyonglong

On Thu, May 25, 2023 at 10:41:51AM +0800, lihuisong (C) wrote:
> Hi Sudeep,
> 
> Here, the interface is used to determine whether a port is in use or
> enabled.
> If we just use 'status', it cannot inidicates its own meaning by name.
> What do you think?
> 

How about "port_status" or "port-status" ?

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-25  7:35         ` Sudeep Holla
@ 2023-05-25  8:12           ` lihuisong (C)
  2023-05-30  2:53             ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-25  8:12 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong


在 2023/5/25 15:35, Sudeep Holla 写道:
> On Thu, May 25, 2023 at 10:41:51AM +0800, lihuisong (C) wrote:
>> Hi Sudeep,
>>
>> Here, the interface is used to determine whether a port is in use or
>> enabled.
>> If we just use 'status', it cannot inidicates its own meaning by name.
>> What do you think?
>>
> How about "port_status" or "port-status" ?
The meaning of this status is a little board.
How about 'enable'? just a read-only entry.


In addition, I happen to have another problem when replace ioremap() 
with acpi_os_ioremap().

The driver selects 'M' in .config to compile.
The driver cannot be compiled if we use acpi_os_ioremap().
The compiling log is as follows:
-->
make -j80
   CALL    scripts/checksyscalls.sh
   CC [M]  drivers/soc/hisilicon/kunpeng_hccs.o
   MODPOST Module.symvers
ERROR: modpost: "acpi_os_ioremap" 
[drivers/soc/hisilicon/kunpeng_hccs.ko] undefined!
scripts/Makefile.modpost:136: recipe for target 'Module.symvers' failed
make[1]: *** [Module.symvers] Error 1
Makefile:1978: recipe for target 'modpost' failed
make: *** [modpost] Error 2

The driver can be compiled if we selects 'Y' or export symbol for 
acpi_os_ioremap.
So we have to export symbol for acpi_os_ioremap.
I plan to do it in another patch, and not in this series in case of 
blocking the upload of this driver.
What do you think?

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-25  8:12           ` lihuisong (C)
@ 2023-05-30  2:53             ` lihuisong (C)
  2023-05-30  8:43               ` Sudeep Holla
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-05-30  2:53 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong

Hi Sudeep,


在 2023/5/25 16:12, lihuisong (C) 写道:
>
> 在 2023/5/25 15:35, Sudeep Holla 写道:
>> On Thu, May 25, 2023 at 10:41:51AM +0800, lihuisong (C) wrote:
>>> Hi Sudeep,
>>>
>>> Here, the interface is used to determine whether a port is in use or
>>> enabled.
>>> If we just use 'status', it cannot inidicates its own meaning by name.
>>> What do you think?
>>>
>> How about "port_status" or "port-status" ?
> The meaning of this status is a little board.
> How about 'enable'? just a read-only entry.
>
>
"using_status" --> "enable" ? What do you think?
Its original purpose was to determine whether a port is in use or enabled.

> In addition, I happen to have another problem when replace ioremap() 
> with acpi_os_ioremap().
>
> The driver selects 'M' in .config to compile.
> The driver cannot be compiled if we use acpi_os_ioremap().
> The compiling log is as follows:
> -->
> make -j80
>   CALL    scripts/checksyscalls.sh
>   CC [M]  drivers/soc/hisilicon/kunpeng_hccs.o
>   MODPOST Module.symvers
> ERROR: modpost: "acpi_os_ioremap" 
> [drivers/soc/hisilicon/kunpeng_hccs.ko] undefined!
> scripts/Makefile.modpost:136: recipe for target 'Module.symvers' failed
> make[1]: *** [Module.symvers] Error 1
> Makefile:1978: recipe for target 'modpost' failed
> make: *** [modpost] Error 2
>
> The driver can be compiled if we selects 'Y' or export symbol for 
> acpi_os_ioremap.
> So we have to export symbol for acpi_os_ioremap.
> I plan to do it in another patch, and not in this series in case of 
> blocking the upload of this driver.
> What do you think?
Someone disagree we do it here. please see the patch[1].
So we have to keep what it was.

[1] 
https://lore.kernel.org/linux-arm-kernel/d180192a-afad-00dc-426f-3d8d249cdd89@huawei.com/T/

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-30  2:53             ` lihuisong (C)
@ 2023-05-30  8:43               ` Sudeep Holla
  2023-05-30 10:57                 ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Sudeep Holla @ 2023-05-30  8:43 UTC (permalink / raw)
  To: lihuisong (C)
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, Sudeep Holla, soc, wanghuiqiang,
	tanxiaofei, liuyonglong

On Tue, May 30, 2023 at 10:53:38AM +0800, lihuisong (C) wrote:
> Hi Sudeep,
> 
> 
> 在 2023/5/25 16:12, lihuisong (C) 写道:
> > 
> > 在 2023/5/25 15:35, Sudeep Holla 写道:
> > > On Thu, May 25, 2023 at 10:41:51AM +0800, lihuisong (C) wrote:
> > > > Hi Sudeep,
> > > > 
> > > > Here, the interface is used to determine whether a port is in use or
> > > > enabled.
> > > > If we just use 'status', it cannot inidicates its own meaning by name.
> > > > What do you think?
> > > > 
> > > How about "port_status" or "port-status" ?
> > The meaning of this status is a little board.
> > How about 'enable'? just a read-only entry.
> > 
> > 
> "using_status" --> "enable" ? What do you think?
> Its original purpose was to determine whether a port is in use or enabled.
>

That's fine. The main point I was trying to make is plain "status" or
"using_status" is prone to conflict as you have ports, lanes, ...etc.
Adding "port_" to the name whatever you choose is better.

-- 
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v2 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-30  8:43               ` Sudeep Holla
@ 2023-05-30 10:57                 ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-05-30 10:57 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, linux-kernel, soc, wanghuiqiang, tanxiaofei,
	liuyonglong


在 2023/5/30 16:43, Sudeep Holla 写道:
> On Tue, May 30, 2023 at 10:53:38AM +0800, lihuisong (C) wrote:
>> Hi Sudeep,
>>
>>
>> 在 2023/5/25 16:12, lihuisong (C) 写道:
>>> 在 2023/5/25 15:35, Sudeep Holla 写道:
>>>> On Thu, May 25, 2023 at 10:41:51AM +0800, lihuisong (C) wrote:
>>>>> Hi Sudeep,
>>>>>
>>>>> Here, the interface is used to determine whether a port is in use or
>>>>> enabled.
>>>>> If we just use 'status', it cannot inidicates its own meaning by name.
>>>>> What do you think?
>>>>>
>>>> How about "port_status" or "port-status" ?
>>> The meaning of this status is a little board.
>>> How about 'enable'? just a read-only entry.
>>>
>>>
>> "using_status" --> "enable" ? What do you think?
>> Its original purpose was to determine whether a port is in use or enabled.
>>
> That's fine. The main point I was trying to make is plain "status" or
ok, create it as "enable".
> "using_status" is prone to conflict as you have ports, lanes, ...etc.
> Adding "port_" to the name whatever you choose is better.
Yes, adding "port_" is better if not all sysfs entries are related to 
the port.
Actually, all sysfs entries belong to the hccsX port under the hccsX 
directory.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (2 preceding siblings ...)
  2023-05-22  7:22 ` [PATCH v2 0/2] " Huisong Li
@ 2023-05-30 11:27 ` Huisong Li
  2023-05-30 11:27   ` [PATCH v3 1/2] " Huisong Li
                     ` (4 more replies)
  2023-07-25  7:57 ` [PATCH RESEND " Huisong Li
                   ` (4 subsequent siblings)
  8 siblings, 5 replies; 79+ messages in thread
From: Huisong Li @ 2023-05-30 11:27 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v3:
  - replace "using_status" with "enable" attribute.
  - fix some comments in codes.

---
 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (2):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   76 +
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   19 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1288 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
 8 files changed, 1590 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-05-30 11:27   ` Huisong Li
  2023-05-30 11:27   ` [PATCH v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-05-30 11:27 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, lihuisong

The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
for ensuring cache coherent on HiSilicon SoC. The performance of
the application may be affected if some hccs ports are in non-full
lane status, have a large number of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   19 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1288 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
 7 files changed, 1513 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 852887c99d3f..e08aa0d5c491 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9430,6 +9430,12 @@ S:	Maintained
 F:	Documentation/ABI/testing/debugfs-hisi-zip
 F:	drivers/crypto/hisilicon/zip/
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HMM - Heterogeneous Memory Management
 M:	Jérôme Glisse <jglisse@redhat.com>
 L:	linux-mm@kvack.org
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 3b0f9fb3b5c8..531f46f3ad98 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..87a1f15cbedb
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
+	  for ensuring cache coherent on HiSilicon SoC. The performance of
+	  the application may be affected if some hccs ports are in non-full
+	  lane status, have a large number of CRC errors and so on.
+
+	  Say M here if you want to include support for querying the health
+	  status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..d7f4bb04deb3
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1288 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
+ * ensuring cache coherent on HiSilicon SoC.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve the following information about each port:
+ *    - port type
+ *    - lane mode
+ *    - enable
+ *    - current lane mode
+ *    - link state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve the following information about all the ports on the chip or
+ *   the die:
+ *    - if all enabled ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->enable = attrs[i].enable;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr =
+			__ATTR(type, 0444, type_show, NULL);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr =
+			__ATTR(lane_mode, 0444, lane_mode_show, NULL);
+
+static ssize_t enable_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->enable);
+}
+
+static struct kobj_attribute port_enable_attr =
+			__ATTR(enable, 0444, enable_show, NULL);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr =
+			__ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr =
+			__ATTR(link_fsm, 0444, link_fsm_show, NULL);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr =
+			__ATTR(lane_mask, 0444, lane_mask_show, NULL);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr =
+			__ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&port_enable_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1", },
+};
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..f9a79904d9c7
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool enable; /* if the port is enabled */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 enable : 1; /* if the port is enabled */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-05-30 11:27   ` [PATCH v3 1/2] " Huisong Li
@ 2023-05-30 11:27   ` Huisong Li
  2023-06-19  6:32   ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC lihuisong (C)
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-05-30 11:27 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 76 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 77 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..a9b5c5b0a997
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,76 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.4
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this chip are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.4
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this die are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.4
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+
+		The HCCS port have the following attributes:
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		enable:       (RO) indicate if this port is enabled (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
+		Note: type, lane_mode and enable are fixed attributes on
+		      running platform.
diff --git a/MAINTAINERS b/MAINTAINERS
index e08aa0d5c491..1da0daece29a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9433,6 +9433,7 @@ F:	drivers/crypto/hisilicon/zip/
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-05-30 11:27   ` [PATCH v3 1/2] " Huisong Li
  2023-05-30 11:27   ` [PATCH v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
@ 2023-06-19  6:32   ` lihuisong (C)
  2023-07-14  6:17   ` lihuisong (C)
  2023-07-20 12:43   ` lihuisong (C)
  4 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-06-19  6:32 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong

Hi all,

Kindly ping.


在 2023/5/30 19:27, Huisong Li 写道:
> This series add HCCS driver to query the health status and port information
> of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
> this driver.
>
> ---
>   v3:
>    - replace "using_status" with "enable" attribute.
>    - fix some comments in codes.
>
> ---
>   v2:
>    - Document all sysfs entries provided by driver.
>    - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
>    - using _CRS with PCC GAS to get channel ID instead of _DSD.
>    - replace readw_relaxed_poll_timeout with readw_poll_timeout.
>    - use sysfs_emit() instead of sprintf().
>    - drop ACPI_PTR in hccs_driver.
>    - drop useless log during the probe phase.
>
> Huisong Li (2):
>    soc: hisilicon: Support HCCS driver on Kunpeng SoC
>    doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
>
>   .../sysfs-devices-platform-kunpeng_hccs       |   76 +
>   MAINTAINERS                                   |    7 +
>   drivers/soc/Kconfig                           |    1 +
>   drivers/soc/Makefile                          |    1 +
>   drivers/soc/hisilicon/Kconfig                 |   19 +
>   drivers/soc/hisilicon/Makefile                |    2 +
>   drivers/soc/hisilicon/kunpeng_hccs.c          | 1288 +++++++++++++++++
>   drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
>   8 files changed, 1590 insertions(+)
>   create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>   create mode 100644 drivers/soc/hisilicon/Kconfig
>   create mode 100644 drivers/soc/hisilicon/Makefile
>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                     ` (2 preceding siblings ...)
  2023-06-19  6:32   ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC lihuisong (C)
@ 2023-07-14  6:17   ` lihuisong (C)
  2023-07-17 12:06     ` Krzysztof Kozlowski
  2023-07-20 12:43   ` lihuisong (C)
  4 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-07-14  6:17 UTC (permalink / raw)
  To: andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong

Hi all,

Can you take a look at this series?


在 2023/5/30 19:27, Huisong Li 写道:
> This series add HCCS driver to query the health status and port information
> of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
> this driver.
>
> ---
>   v3:
>    - replace "using_status" with "enable" attribute.
>    - fix some comments in codes.
>
> ---
>   v2:
>    - Document all sysfs entries provided by driver.
>    - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
>    - using _CRS with PCC GAS to get channel ID instead of _DSD.
>    - replace readw_relaxed_poll_timeout with readw_poll_timeout.
>    - use sysfs_emit() instead of sprintf().
>    - drop ACPI_PTR in hccs_driver.
>    - drop useless log during the probe phase.
>
> Huisong Li (2):
>    soc: hisilicon: Support HCCS driver on Kunpeng SoC
>    doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
>
>   .../sysfs-devices-platform-kunpeng_hccs       |   76 +
>   MAINTAINERS                                   |    7 +
>   drivers/soc/Kconfig                           |    1 +
>   drivers/soc/Makefile                          |    1 +
>   drivers/soc/hisilicon/Kconfig                 |   19 +
>   drivers/soc/hisilicon/Makefile                |    2 +
>   drivers/soc/hisilicon/kunpeng_hccs.c          | 1288 +++++++++++++++++
>   drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
>   8 files changed, 1590 insertions(+)
>   create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>   create mode 100644 drivers/soc/hisilicon/Kconfig
>   create mode 100644 drivers/soc/hisilicon/Makefile
>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-14  6:17   ` lihuisong (C)
@ 2023-07-17 12:06     ` Krzysztof Kozlowski
  2023-07-18  8:07       ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-17 12:06 UTC (permalink / raw)
  To: lihuisong (C),
	andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, sudeep.holla
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong

On 14/07/2023 08:17, lihuisong (C) wrote:
> Hi all,
> 
> Can you take a look at this series?

People you listed in "To:" field are not maintainers of HiSilicon, so
why pinging us?

HiSilicon DTS has numerous issues, so if you want to increase the
chances anyone cares about your patch, please contribute to fixing and
improving your company DTS. See for example commit b2bbc8687 which is
bad (underscores are not allowed) or 1860a51823 which is just wrong. The
latter one is being fixed, although I do not see the fix being picked
up... my feelings are the platform is just dead.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-17 12:06     ` Krzysztof Kozlowski
@ 2023-07-18  8:07       ` lihuisong (C)
  2023-07-18 10:59         ` Krzysztof Kozlowski
  2023-07-18 11:01         ` Wei Xu
  0 siblings, 2 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-07-18  8:07 UTC (permalink / raw)
  To: Krzysztof Kozlowski, xuwei (O)
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, sudeep.holla, linux-arm-kernel

Hi Krzysztof,

Thank you for your reply.


在 2023/7/17 20:06, Krzysztof Kozlowski 写道:
> On 14/07/2023 08:17, lihuisong (C) wrote:
>> Hi all,
>>
>> Can you take a look at this series?
> People you listed in "To:" field are not maintainers of HiSilicon, so
> why pinging us?
This "To:" field are from the get_maintainer.pl script.
./scripts/get_maintainer.pl -f ./drivers/soc/
The hisilicon in drivers/soc/ is created for the first time. So there 
are no maintainer of HiSilicon.

Thanks for reminder.
add HiSilicon SOC maintainer Wei Xu.
>
> HiSilicon DTS has numerous issues, so if you want to increase the
> chances anyone cares about your patch, please contribute to fixing and
> improving your company DTS. See for example commit b2bbc8687 which is
> bad (underscores are not allowed) or 1860a51823 which is just wrong. The
> latter one is being fixed, although I do not see the fix being picked
> up... my feelings are the platform is just dead.
The HCCS driver has nothing to do with HiSilicon DTS.
So I cannot understand why you say DTS.

/Huisong

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-18  8:07       ` lihuisong (C)
@ 2023-07-18 10:59         ` Krzysztof Kozlowski
  2023-07-18 14:00           ` lihuisong (C)
  2023-07-18 11:01         ` Wei Xu
  1 sibling, 1 reply; 79+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-18 10:59 UTC (permalink / raw)
  To: lihuisong (C), xuwei (O)
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, sudeep.holla, linux-arm-kernel

On 18/07/2023 10:07, lihuisong (C) wrote:
> Hi Krzysztof,
> 
> Thank you for your reply.
> 
> 
> 在 2023/7/17 20:06, Krzysztof Kozlowski 写道:
>> On 14/07/2023 08:17, lihuisong (C) wrote:
>>> Hi all,
>>>
>>> Can you take a look at this series?
>> People you listed in "To:" field are not maintainers of HiSilicon, so
>> why pinging us?
> This "To:" field are from the get_maintainer.pl script.
> ./scripts/get_maintainer.pl -f ./drivers/soc/

Which is not really correct. Why would you send it to maintainers of
Samsung or NXP?

> The hisilicon in drivers/soc/ is created for the first time. So there 
> are no maintainer of HiSilicon.

There are. Open MAINTAINERS file, use search and voila.

> 
> Thanks for reminder.
> add HiSilicon SOC maintainer Wei Xu.
>>
>> HiSilicon DTS has numerous issues, so if you want to increase the
>> chances anyone cares about your patch, please contribute to fixing and
>> improving your company DTS. See for example commit b2bbc8687 which is
>> bad (underscores are not allowed) or 1860a51823 which is just wrong. The
>> latter one is being fixed, although I do not see the fix being picked
>> up... my feelings are the platform is just dead.
> The HCCS driver has nothing to do with HiSilicon DTS.
> So I cannot understand why you say DTS.

I grunted about poor support for HiSilicon. Because both - HiSilicon DTS
and HiSilicon SoC HCCS driver - are for HiSilicon.

Now, why don't you help the maintainers review/fix stuff so they will
have more time for your patch?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-18  8:07       ` lihuisong (C)
  2023-07-18 10:59         ` Krzysztof Kozlowski
@ 2023-07-18 11:01         ` Wei Xu
  1 sibling, 0 replies; 79+ messages in thread
From: Wei Xu @ 2023-07-18 11:01 UTC (permalink / raw)
  To: lihuisong (C), Krzysztof Kozlowski, xuwei (O)
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, sudeep.holla, linux-arm-kernel

Hi Krzysztof,

On 2023/7/18 16:07, lihuisong (C) wrote:
> Hi Krzysztof,
> 
> Thank you for your reply.
> 
> 
> 在 2023/7/17 20:06, Krzysztof Kozlowski 写道:
>> On 14/07/2023 08:17, lihuisong (C) wrote:
>>> Hi all,
>>>
>>> Can you take a look at this series?
>> People you listed in "To:" field are not maintainers of HiSilicon, so
>> why pinging us?
> This "To:" field are from the get_maintainer.pl script.
> ./scripts/get_maintainer.pl -f ./drivers/soc/
> The hisilicon in drivers/soc/ is created for the first time. So there are no maintainer of HiSilicon.
> 
> Thanks for reminder.
> add HiSilicon SOC maintainer Wei Xu.
>>
>> HiSilicon DTS has numerous issues, so if you want to increase the
>> chances anyone cares about your patch, please contribute to fixing and
>> improving your company DTS. See for example commit b2bbc8687 which is
>> bad (underscores are not allowed) or 1860a51823 which is just wrong. The
>> latter one is being fixed, although I do not see the fix being picked

Sorry, it seems wrong of the commit 1860a51823 which changed the clock pin.
I have checked the mail list but did not find the bug fix for it.
Could you please kindly share the subject of the patch?
Or I can simply revert it. 
Thanks!

Best Regards,
Wei

>> up... my feelings are the platform is just dead.
> The HCCS driver has nothing to do with HiSilicon DTS.
> So I cannot understand why you say DTS.
> 
> /Huisong
> .
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-18 10:59         ` Krzysztof Kozlowski
@ 2023-07-18 14:00           ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-07-18 14:00 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong,
	andersson, matthias.bgg, angelogioacchino.delregno, shawnguo,
	arnd, sudeep.holla, linux-arm-kernel, lihuisong, xuwei (O)


在 2023/7/18 18:59, Krzysztof Kozlowski 写道:
> On 18/07/2023 10:07, lihuisong (C) wrote:
>> Hi Krzysztof,
>>
>> Thank you for your reply.
>>
>>
>> 在 2023/7/17 20:06, Krzysztof Kozlowski 写道:
>>> On 14/07/2023 08:17, lihuisong (C) wrote:
>>>> Hi all,
>>>>
>>>> Can you take a look at this series?
>>> People you listed in "To:" field are not maintainers of HiSilicon, so
>>> why pinging us?
>> This "To:" field are from the get_maintainer.pl script.
>> ./scripts/get_maintainer.pl -f ./drivers/soc/
> Which is not really correct. Why would you send it to maintainers of
> Samsung or NXP?
Yeah, that's not entirely correct.
I should skip other SOC maintainers from the output of the script.
The reason why I send to some maintainers in "To" field is that some of 
them have kindly reviewed my patch before.
>
>> The hisilicon in drivers/soc/ is created for the first time. So there
>> are no maintainer of HiSilicon.
> There are. Open MAINTAINERS file, use search and voila.
got it, thanks.
>
>> Thanks for reminder.
>> add HiSilicon SOC maintainer Wei Xu.
>>> HiSilicon DTS has numerous issues, so if you want to increase the
>>> chances anyone cares about your patch, please contribute to fixing and
>>> improving your company DTS. See for example commit b2bbc8687 which is
>>> bad (underscores are not allowed) or 1860a51823 which is just wrong. The
>>> latter one is being fixed, although I do not see the fix being picked
>>> up... my feelings are the platform is just dead.
>> The HCCS driver has nothing to do with HiSilicon DTS.
>> So I cannot understand why you say DTS.
> I grunted about poor support for HiSilicon. Because both - HiSilicon DTS
> and HiSilicon SoC HCCS driver - are for HiSilicon.

This driver works on kunpneg SOC platform, which isn't related to 
something you mentioned.

>
> Now, why don't you help the maintainers review/fix stuff so they will
> have more time for your patch?
Yes, this is helpful and reasonable. Would do it if I have the chance.

Best regards,
Huisong

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                     ` (3 preceding siblings ...)
  2023-07-14  6:17   ` lihuisong (C)
@ 2023-07-20 12:43   ` lihuisong (C)
  4 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-07-20 12:43 UTC (permalink / raw)
  To: arnd, sudeep.holla, krzk
  Cc: linux-kernel, soc, wanghuiqiang, tanxiaofei, liuyonglong, xuwei5

Hi All,

Now I add HiSilicon SOC maintainer Wei Xu attributing to Krzysztof's 
reminder.

Thanks for your review. All comments have been closed.
Could you please kindly confirm my modifications for your comments?
If it is ok for you. In order to go on smoothly, can you reply?😁

Best regards,
Huisong


在 2023/5/30 19:27, Huisong Li 写道:
> This series add HCCS driver to query the health status and port information
> of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
> this driver.
>
> ---
>   v3:
>    - replace "using_status" with "enable" attribute.
>    - fix some comments in codes.
>
> ---
>   v2:
>    - Document all sysfs entries provided by driver.
>    - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
>    - using _CRS with PCC GAS to get channel ID instead of _DSD.
>    - replace readw_relaxed_poll_timeout with readw_poll_timeout.
>    - use sysfs_emit() instead of sprintf().
>    - drop ACPI_PTR in hccs_driver.
>    - drop useless log during the probe phase.
>
> Huisong Li (2):
>    soc: hisilicon: Support HCCS driver on Kunpeng SoC
>    doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
>
>   .../sysfs-devices-platform-kunpeng_hccs       |   76 +
>   MAINTAINERS                                   |    7 +
>   drivers/soc/Kconfig                           |    1 +
>   drivers/soc/Makefile                          |    1 +
>   drivers/soc/hisilicon/Kconfig                 |   19 +
>   drivers/soc/hisilicon/Makefile                |    2 +
>   drivers/soc/hisilicon/kunpeng_hccs.c          | 1288 +++++++++++++++++
>   drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
>   8 files changed, 1590 insertions(+)
>   create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>   create mode 100644 drivers/soc/hisilicon/Kconfig
>   create mode 100644 drivers/soc/hisilicon/Makefile
>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH RESEND v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (3 preceding siblings ...)
  2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-07-25  7:57 ` Huisong Li
  2023-07-25  7:57   ` [PATCH RESEND v3 1/2] " Huisong Li
  2023-07-25  7:57   ` [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  2023-07-28  3:03 ` [PATCH v4 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (3 subsequent siblings)
  8 siblings, 2 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-25  7:57 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v3:
  - replace "using_status" with "enable" attribute.
  - fix some comments in codes.

 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (2):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   76 +
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   19 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1288 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
 8 files changed, 1590 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH RESEND v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-25  7:57 ` [PATCH RESEND " Huisong Li
@ 2023-07-25  7:57   ` Huisong Li
  2023-07-25  8:55     ` Wei Xu
  2023-07-25 15:28     ` Randy Dunlap
  2023-07-25  7:57   ` [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  1 sibling, 2 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-25  7:57 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
for ensuring cache coherent on HiSilicon SoC. The performance of
the application may be affected if some hccs ports are in non-full
lane status, have a large number of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   19 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1288 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
 7 files changed, 1513 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index a5c16bb92fe2..4e55ff992171 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9471,6 +9471,12 @@ S:	Maintained
 F:	Documentation/ABI/testing/debugfs-hisi-zip
 F:	drivers/crypto/hisilicon/zip/
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HMM - Heterogeneous Memory Management
 M:	Jérôme Glisse <jglisse@redhat.com>
 L:	linux-mm@kvack.org
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 3b0f9fb3b5c8..531f46f3ad98 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..87a1f15cbedb
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
+	  for ensuring cache coherent on HiSilicon SoC. The performance of
+	  the application may be affected if some hccs ports are in non-full
+	  lane status, have a large number of CRC errors and so on.
+
+	  Say M here if you want to include support for querying the health
+	  status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..d7f4bb04deb3
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1288 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
+ * ensuring cache coherent on HiSilicon SoC.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve the following information about each port:
+ *    - port type
+ *    - lane mode
+ *    - enable
+ *    - current lane mode
+ *    - link state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve the following information about all the ports on the chip or
+ *   the die:
+ *    - if all enabled ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->enable = attrs[i].enable;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr =
+			__ATTR(type, 0444, type_show, NULL);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr =
+			__ATTR(lane_mode, 0444, lane_mode_show, NULL);
+
+static ssize_t enable_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->enable);
+}
+
+static struct kobj_attribute port_enable_attr =
+			__ATTR(enable, 0444, enable_show, NULL);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr =
+			__ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr =
+			__ATTR(link_fsm, 0444, link_fsm_show, NULL);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr =
+			__ATTR(lane_mask, 0444, lane_mask_show, NULL);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr =
+			__ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&port_enable_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1", },
+};
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..f9a79904d9c7
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool enable; /* if the port is enabled */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 enable : 1; /* if the port is enabled */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-07-25  7:57 ` [PATCH RESEND " Huisong Li
  2023-07-25  7:57   ` [PATCH RESEND v3 1/2] " Huisong Li
@ 2023-07-25  7:57   ` Huisong Li
  2023-07-25  8:59     ` Wei Xu
  1 sibling, 1 reply; 79+ messages in thread
From: Huisong Li @ 2023-07-25  7:57 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 76 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 77 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..83ebed801249
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,76 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this chip are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this die are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		May 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+
+		The HCCS port have the following attributes:
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		enable:       (RO) indicate if this port is enabled (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
+		Note: type, lane_mode and enable are fixed attributes on
+		      running platform.
diff --git a/MAINTAINERS b/MAINTAINERS
index 4e55ff992171..7a34bab232eb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9474,6 +9474,7 @@ F:	drivers/crypto/hisilicon/zip/
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-25  7:57   ` [PATCH RESEND v3 1/2] " Huisong Li
@ 2023-07-25  8:55     ` Wei Xu
  2023-07-26  9:54       ` lihuisong (C)
  2023-07-25 15:28     ` Randy Dunlap
  1 sibling, 1 reply; 79+ messages in thread
From: Wei Xu @ 2023-07-25  8:55 UTC (permalink / raw)
  To: Huisong Li, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong

Hi Huisong,

On 2023/7/25 15:57, Huisong Li wrote:
> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> for ensuring cache coherent on HiSilicon SoC. The performance of
> the application may be affected if some hccs ports are in non-full
> lane status, have a large number of CRC errors and so on.
> 
> This driver provides the query interface of the health status and
> port information of HCCS on Kunpeng SoC.
> 
> Signed-off-by: Huisong Li <lihuisong@huawei.com>
> ---
>  MAINTAINERS                          |    6 +
>  drivers/soc/Kconfig                  |    1 +
>  drivers/soc/Makefile                 |    1 +
>  drivers/soc/hisilicon/Kconfig        |   19 +
>  drivers/soc/hisilicon/Makefile       |    2 +
>  drivers/soc/hisilicon/kunpeng_hccs.c | 1288 ++++++++++++++++++++++++++
>  drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>  7 files changed, 1513 insertions(+)
>  create mode 100644 drivers/soc/hisilicon/Kconfig
>  create mode 100644 drivers/soc/hisilicon/Makefile
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a5c16bb92fe2..4e55ff992171 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9471,6 +9471,12 @@ S:	Maintained
>  F:	Documentation/ABI/testing/debugfs-hisi-zip
>  F:	drivers/crypto/hisilicon/zip/
>  
> +HISILICON KUNPENG SOC HCCS DRIVER
> +M:	Huisong Li <lihuisong@huawei.com>
> +S:	Maintained
> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
> +
>  HMM - Heterogeneous Memory Management
>  M:	Jérôme Glisse <jglisse@redhat.com>
>  L:	linux-mm@kvack.org
> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
> index 4e176280113a..d21e75d69294 100644
> --- a/drivers/soc/Kconfig
> +++ b/drivers/soc/Kconfig
> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>  source "drivers/soc/canaan/Kconfig"
>  source "drivers/soc/fsl/Kconfig"
>  source "drivers/soc/fujitsu/Kconfig"
> +source "drivers/soc/hisilicon/Kconfig"
>  source "drivers/soc/imx/Kconfig"
>  source "drivers/soc/ixp4xx/Kconfig"
>  source "drivers/soc/litex/Kconfig"
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 3b0f9fb3b5c8..531f46f3ad98 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>  obj-y				+= fsl/
>  obj-y				+= fujitsu/
>  obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
> +obj-y				+= hisilicon/
>  obj-y				+= imx/
>  obj-y				+= ixp4xx/
>  obj-$(CONFIG_SOC_XWAY)		+= lantiq/
> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
> new file mode 100644
> index 000000000000..87a1f15cbedb
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Kconfig
> @@ -0,0 +1,19 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Hisilicon SoC drivers"
> +	depends on ARCH_HISI || COMPILE_TEST
> +
> +config KUNPENG_HCCS
> +	tristate "HCCS driver on Kunpeng SoC"
> +	depends on ACPI
> +	depends on ARM64 || COMPILE_TEST
> +	help
> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
> +	  the application may be affected if some hccs ports are in non-full
> +	  lane status, have a large number of CRC errors and so on.
> +
> +	  Say M here if you want to include support for querying the health
> +	  status and port information of HCCS on Kunpeng SoC.
> +
> +endmenu
> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
> new file mode 100644
> index 000000000000..226e747e70d6
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
> new file mode 100644
> index 000000000000..d7f4bb04deb3
> --- /dev/null
> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
> @@ -0,0 +1,1288 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
> + * ensuring cache coherent on HiSilicon SoC.
> + *
> + * Copyright (c) 2023 Hisilicon Limited.
> + * Author: Huisong Li <lihuisong@huawei.com>
> + *
> + * HCCS driver for Kunpeng SoC provides the following features:
> + * - Retrieve the following information about each port:
> + *    - port type
> + *    - lane mode
> + *    - enable
> + *    - current lane mode
> + *    - link state machine
> + *    - lane mask
> + *    - CRC error count
> + *
> + * - Retrieve the following information about all the ports on the chip or
> + *   the die:
> + *    - if all enabled ports are in linked
> + *    - if all linked ports are in full lane
> + *    - CRC error count sum
> + */
> +#include <linux/sysfs.h>
> +#include <linux/acpi.h>
> +#include <linux/io.h>
> +#include <linux/kobject.h>

Please remove the useless including of the io.h and kobject.h.

> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>

It is better to put all the linux headers into alphabetical order.

> +#include <acpi/pcc.h>
> +
> +#include "kunpeng_hccs.h"
> +
> +/* PCC defines */
> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
> +
> +/*
> + * Arbitrary retries in case the remote processor is slow to respond
> + * to PCC commands
> + */
> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
> +
> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_port_info, kobj);
> +}
> +
> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_die_info, kobj);
> +}
> +
> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
> +{
> +	return container_of(k, struct hccs_chip_info, kobj);
> +}
> +
> +struct hccs_register_ctx {
> +	struct device *dev;
> +	u8 chan_id;
> +	int err;
> +};
> +
> +static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
> +					void *context)
> +{
> +	struct acpi_resource_generic_register *reg;
> +	struct hccs_register_ctx *ctx = context;
> +
> +	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
> +		return AE_OK;
> +
> +	reg = &ares->data.generic_reg;
> +	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
> +		dev_err(ctx->dev, "Bad register resource.\n");
> +		ctx->err = -EINVAL;
> +		return AE_ERROR;
> +	}
> +	ctx->chan_id = reg->access_size;
> +
> +	return AE_OK;
> +}
> +
> +static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
> +{
> +	acpi_handle handle = ACPI_HANDLE(hdev->dev);
> +	struct hccs_register_ctx ctx = {0};
> +	acpi_status status;
> +
> +	if (!acpi_has_method(handle, METHOD_NAME__CRS))
> +		return -ENODEV;
> +
> +	ctx.dev = hdev->dev;
> +	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
> +				     hccs_get_register_cb, &ctx);
> +	if (ACPI_FAILURE(status))
> +		return ctx.err;
> +	hdev->chan_id = ctx.chan_id;
> +
> +	return 0;
> +}
> +
> +static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
> +{
> +	if (ret < 0)
> +		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
> +			 *(u8 *)msg, ret);
> +	else
> +		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
> +			 *(u8 *)msg, ret);
> +}
> +
> +static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +
> +	if (cl_info->pcc_comm_addr)
> +		iounmap(cl_info->pcc_comm_addr);
> +	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
> +}
> +
> +static int hccs_register_pcc_channel(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct mbox_client *cl = &cl_info->client;
> +	struct pcc_mbox_chan *pcc_chan;
> +	struct device *dev = hdev->dev;
> +	int rc;
> +
> +	cl->dev = dev;
> +	cl->tx_block = false;
> +	cl->knows_txdone = true;
> +	cl->tx_done = hccs_chan_tx_done;
> +	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
> +	if (IS_ERR(pcc_chan)) {
> +		dev_err(dev, "PPC channel request failed.\n");
> +		rc = -ENODEV;
> +		goto out;
> +	}
> +	cl_info->pcc_chan = pcc_chan;
> +	cl_info->mbox_chan = pcc_chan->mchan;
> +
> +	/*
> +	 * pcc_chan->latency is just a nominal value. In reality the remote
> +	 * processor could be much slower to reply. So add an arbitrary amount
> +	 * of wait on top of nominal.
> +	 */
> +	cl_info->deadline_us =
> +			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
> +	if (cl_info->mbox_chan->mbox->txdone_irq) {
> +		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
> +		rc = -EINVAL;
> +		goto err_mbx_channel_free;
> +	}
> +
> +	if (pcc_chan->shmem_base_addr) {
> +		cl_info->pcc_comm_addr = (void __force *)ioremap(
> +			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
> +		if (!cl_info->pcc_comm_addr) {
> +			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
> +				hdev->chan_id);
> +			rc = -ENOMEM;
> +			goto err_mbx_channel_free;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_mbx_channel_free:
> +	pcc_mbox_free_channel(cl_info->pcc_chan);
> +out:
> +	return rc;
> +}
> +
> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
> +	u16 status;
> +	int ret;
> +
> +	/*
> +	 * Poll PCC status register every 3us(delay_us) for maximum of
> +	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
> +	 */
> +	ret = readw_poll_timeout(&comm_base->status, status,
> +				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
> +				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
> +				 cl_info->deadline_us);
> +	if (unlikely(ret))
> +		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
> +
> +	return ret;
> +}
> +
> +static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
> +			     struct hccs_desc *desc)
> +{
> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
> +	void *comm_space = (void *)(comm_base + 1);
> +	struct hccs_fw_inner_head *fw_inner_head;
> +	struct acpi_pcct_shared_memory tmp = {0};
> +	u16 comm_space_size;
> +	int ret;
> +
> +	/* Write signature for this subspace */
> +	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
> +	/* Write to the shared command region */
> +	tmp.command = cmd;
> +	/* Clear cmd complete bit */
> +	tmp.status = 0;
> +	memcpy_toio(comm_base, (void *)&tmp,
> +			sizeof(struct acpi_pcct_shared_memory));
> +
> +	/* Copy the message to the PCC comm space */
> +	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
> +				sizeof(struct acpi_pcct_shared_memory);
> +	memcpy_toio(comm_space, (void *)desc, comm_space_size);
> +
> +	/* Ring doorbell */
> +	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
> +	if (ret < 0) {
> +		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
> +			ret);
> +		goto end;
> +	}
> +
> +	/* Wait for completion */
> +	ret = hccs_check_chan_cmd_complete(hdev);
> +	if (ret)
> +		goto end;
> +
> +	/* Copy response data */
> +	memcpy_fromio((void *)desc, comm_space, comm_space_size);
> +	fw_inner_head = &desc->rsp.fw_inner_head;
> +	if (fw_inner_head->retStatus) {
> +		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
> +			fw_inner_head->retStatus);
> +		ret = -EIO;
> +	}
> +
> +end:
> +	mbox_client_txdone(cl_info->mbox_chan, ret);
> +	return ret;
> +}
> +
> +static void hccs_init_req_desc(struct hccs_desc *desc)
> +{
> +	struct hccs_req_desc *req = &desc->req;
> +
> +	memset(desc, 0, sizeof(*desc));
> +	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
> +}
> +
> +static int hccs_get_dev_caps(struct hccs_dev *hdev)
> +{
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	hccs_init_req_desc(&desc);
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
> +			ret);
> +		return ret;
> +	}
> +	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
> +
> +	return 0;
> +}
> +
> +static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
> +{
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	hccs_init_req_desc(&desc);
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	hdev->chip_num = *((u8 *)&desc.rsp.data);
> +	if (!hdev->chip_num) {
> +		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_get_chip_info(struct hccs_dev *hdev,
> +			      struct hccs_chip_info *chip)
> +{
> +	struct hccs_die_num_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_die_num_req_param *)desc.req.data;
> +	req_param->chip_id = chip->chip_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
> +	if (ret)
> +		return ret;
> +
> +	chip->die_num = *((u8 *)&desc.rsp.data);
> +
> +	return 0;
> +}
> +
> +static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
> +{
> +	struct hccs_chip_info *chip;
> +	int ret;
> +	u8 idx;
> +
> +	ret = hccs_query_chip_num_on_platform(hdev);
> +	if (ret) {
> +		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	hdev->chips = devm_kzalloc(hdev->dev,
> +				hdev->chip_num * sizeof(struct hccs_chip_info),
> +				GFP_KERNEL);
> +	if (!hdev->chips) {
> +		dev_err(hdev->dev, "allocate all chips memory failed.\n");
> +		return -ENOMEM;
> +	}
> +
> +	for (idx = 0; idx < hdev->chip_num; idx++) {
> +		chip = &hdev->chips[idx];
> +		chip->chip_id = idx;
> +		ret = hccs_get_chip_info(hdev, chip);
> +		if (ret) {
> +			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
> +				idx, ret);
> +			return ret;
> +		}
> +		chip->hdev = hdev;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
> +				       u8 die_idx, struct hccs_die_info *die)
> +{
> +	struct hccs_die_info_req_param *req_param;
> +	struct hccs_die_info_rsp_data *rsp_data;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_die_info_req_param *)desc.req.data;
> +	req_param->chip_id = chip_id;
> +	req_param->die_idx = die_idx;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
> +	if (ret)
> +		return ret;
> +
> +	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
> +	die->die_id = rsp_data->die_id;
> +	die->port_num = rsp_data->port_num;
> +	die->min_port_id = rsp_data->min_port_id;
> +	die->max_port_id = rsp_data->max_port_id;
> +	if (die->min_port_id > die->max_port_id) {
> +		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
> +			die->min_port_id, die->max_port_id, die_idx);
> +		return -EINVAL;
> +	}
> +	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
> +		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
> +			die->max_port_id, die_idx);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
> +{
> +	struct device *dev = hdev->dev;
> +	struct hccs_chip_info *chip;
> +	struct hccs_die_info *die;
> +	u8 i, j;
> +	int ret;
> +
> +	for (i = 0; i < hdev->chip_num; i++) {
> +		chip = &hdev->chips[i];
> +		if (!chip->die_num)
> +			continue;
> +
> +		chip->dies = devm_kzalloc(hdev->dev,
> +				chip->die_num * sizeof(struct hccs_die_info),
> +				GFP_KERNEL);
> +		if (!chip->dies) {
> +			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
> +				i);
> +			return -ENOMEM;
> +		}
> +
> +		for (j = 0; j < chip->die_num; j++) {
> +			die = &chip->dies[j];
> +			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
> +			if (ret) {
> +				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
> +					j, i, ret);
> +				return ret;
> +			}
> +			die->chip = chip;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
> +			    struct hccs_desc *desc,
> +			    void *buf, size_t buf_len,
> +			    struct hccs_rsp_head *rsp_head)
> +{
> +	struct hccs_rsp_head *head;
> +	struct hccs_rsp_desc *rsp;
> +	int ret;
> +
> +	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
> +	if (ret)
> +		return ret;
> +
> +	rsp = &desc->rsp;
> +	head = &rsp->rsp_head;
> +	if (head->data_len > buf_len) {
> +		dev_err(hdev->dev,
> +			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
> +			buf_len, head->data_len);
> +		return -ENOMEM;
> +	}
> +
> +	memcpy(buf, rsp->data, head->data_len);
> +	*rsp_head = *head;
> +
> +	return 0;
> +}
> +
> +static int hccs_get_all_port_attr(struct hccs_dev *hdev,
> +				  struct hccs_die_info *die,
> +				  struct hccs_port_attr *attrs, u16 size)
> +{
> +	struct hccs_die_comm_req_param *req_param;
> +	struct hccs_req_head *req_head;
> +	struct hccs_rsp_head rsp_head;
> +	struct hccs_desc desc;
> +	size_t left_buf_len;
> +	u32 data_len = 0;
> +	u8 start_id;
> +	u8 *buf;
> +	int ret;
> +
> +	buf = (u8 *)attrs;
> +	left_buf_len = sizeof(struct hccs_port_attr) * size;
> +	start_id = die->min_port_id;
> +	while (start_id <= die->max_port_id) {
> +		hccs_init_req_desc(&desc);
> +		req_head = &desc.req.req_head;
> +		req_head->start_id = start_id;
> +		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
> +		req_param->chip_id = die->chip->chip_id;
> +		req_param->die_id = die->die_id;
> +
> +		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
> +				       buf + data_len, left_buf_len, &rsp_head);
> +		if (ret) {
> +			dev_err(hdev->dev,
> +				"get the information of port%u on die%u failed, ret = %d.\n",
> +				start_id, die->die_id, ret);
> +			return ret;
> +		}
> +
> +		data_len += rsp_head.data_len;
> +		left_buf_len -= rsp_head.data_len;
> +		if (unlikely(rsp_head.next_id <= start_id)) {
> +			dev_err(hdev->dev,
> +				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
> +				rsp_head.next_id, start_id, die->die_id);
> +			return -EINVAL;
> +		}
> +		start_id = rsp_head.next_id;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
> +					 struct hccs_die_info *die)
> +{
> +	struct hccs_port_attr *attrs;
> +	struct hccs_port_info *port;
> +	int ret;
> +	u8 i;
> +
> +	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
> +			GFP_KERNEL);
> +	if (!attrs)
> +		return -ENOMEM;
> +
> +	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
> +	if (ret)
> +		goto out;
> +
> +	for (i = 0; i < die->port_num; i++) {
> +		port = &die->ports[i];
> +		port->port_id = attrs[i].port_id;
> +		port->port_type = attrs[i].port_type;
> +		port->lane_mode = attrs[i].lane_mode;
> +		port->enable = attrs[i].enable;
> +		port->die = die;
> +	}
> +
> +out:
> +	kfree(attrs);
> +	return ret;
> +}
> +
> +static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
> +{
> +
> +	struct device *dev = hdev->dev;
> +	struct hccs_chip_info *chip;
> +	struct hccs_die_info *die;
> +	u8 i, j;
> +	int ret;
> +
> +	for (i = 0; i < hdev->chip_num; i++) {
> +		chip = &hdev->chips[i];
> +		for (j = 0; j < chip->die_num; j++) {
> +			die = &chip->dies[j];
> +			if (!die->port_num)
> +				continue;
> +
> +			die->ports = devm_kzalloc(dev,
> +				die->port_num * sizeof(struct hccs_port_info),
> +				GFP_KERNEL);
> +			if (!die->ports) {
> +				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
> +					i, die->die_id);
> +				return -ENOMEM;
> +			}
> +
> +			ret = hccs_get_all_port_info_on_die(hdev, die);
> +			if (ret) {
> +				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
> +					die->die_id, i, ret);
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_get_hw_info(struct hccs_dev *hdev)
> +{
> +	int ret;
> +
> +	ret = hccs_query_chip_info_on_platform(hdev);
> +	if (ret) {
> +		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	ret = hccs_query_all_die_info_on_platform(hdev);
> +	if (ret) {
> +		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	ret = hccs_query_all_port_info_on_platform(hdev);
> +	if (ret) {
> +		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_query_port_link_status(struct hccs_dev *hdev,
> +				       const struct hccs_port_info *port,
> +				       struct hccs_link_status *link_status)
> +{
> +	const struct hccs_die_info *die = port->die;
> +	const struct hccs_chip_info *chip = die->chip;
> +	struct hccs_port_comm_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
> +	req_param->chip_id = chip->chip_id;
> +	req_param->die_id = die->die_id;
> +	req_param->port_id = port->port_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev,
> +			"get port link status info failed, ret = %d.\n", ret);
> +		return ret;
> +	}
> +
> +	*link_status = *((struct hccs_link_status *)desc.rsp.data);
> +
> +	return 0;
> +}
> +
> +static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
> +				       const struct hccs_port_info *port,
> +				       u64 *crc_err_cnt)
> +{
> +	const struct hccs_die_info *die = port->die;
> +	const struct hccs_chip_info *chip = die->chip;
> +	struct hccs_port_comm_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
> +	req_param->chip_id = chip->chip_id;
> +	req_param->die_id = die->die_id;
> +	req_param->port_id = port->port_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev,
> +			"get port crc error count failed, ret = %d.\n", ret);
> +		return ret;
> +	}
> +
> +	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
> +
> +	return 0;
> +}
> +
> +static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
> +					const struct hccs_die_info *die,
> +					u8 *all_linked)
> +{
> +	struct hccs_die_comm_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	if (die->port_num == 0) {
> +		*all_linked = 1;
> +		return 0;
> +	}
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
> +	req_param->chip_id = die->chip->chip_id;
> +	req_param->die_id = die->die_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev,
> +			"get link status of all ports failed on die%u, ret = %d.\n",
> +			die->die_id, ret);
> +		return ret;
> +	}
> +
> +	*all_linked = *((u8 *)&desc.rsp.data);
> +
> +	return 0;
> +}
> +
> +static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
> +					     const struct hccs_die_info *die,
> +					     u8 *full_lane)
> +{
> +	struct hccs_die_comm_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	if (die->port_num == 0) {
> +		*full_lane = 1;
> +		return 0;
> +	}
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
> +	req_param->chip_id = die->chip->chip_id;
> +	req_param->die_id = die->die_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
> +			die->die_id, ret);
> +		return ret;
> +	}
> +
> +	*full_lane = *((u8 *)&desc.rsp.data);
> +
> +	return 0;
> +}
> +
> +static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
> +					  const struct hccs_die_info *die,
> +					  u64 *total_crc_err_cnt)
> +{
> +	struct hccs_die_comm_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	if (die->port_num == 0) {
> +		*total_crc_err_cnt = 0;
> +		return 0;
> +	}
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
> +	req_param->chip_id = die->chip->chip_id;
> +	req_param->die_id = die->die_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
> +	if (ret) {
> +		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
> +			die->die_id, ret);
> +		return ret;
> +	}
> +
> +	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
> +
> +	return 0;
> +}
> +
> +static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
> +{
> +	struct kobj_attribute *kobj_attr;
> +
> +	kobj_attr = container_of(attr, struct kobj_attribute, attr);
> +
> +	return kobj_attr->show(k, kobj_attr, buf);
> +}
> +
> +static const struct sysfs_ops hccs_comm_ops = {
> +	.show = hccs_show,
> +};
> +
> +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
> +			 char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +
> +	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
> +}
> +
> +static struct kobj_attribute hccs_type_attr =
> +			__ATTR(type, 0444, type_show, NULL);

It is better to use __ATTR_RO.

> +
> +static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
> +			      char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +
> +	return sysfs_emit(buf, "x%u\n", port->lane_mode);
> +}
> +
> +static struct kobj_attribute lane_mode_attr =
> +			__ATTR(lane_mode, 0444, lane_mode_show, NULL);
> +
> +static ssize_t enable_show(struct kobject *kobj,
> +				 struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +
> +	return sysfs_emit(buf, "%u\n", port->enable);
> +}
> +
> +static struct kobj_attribute port_enable_attr =
> +			__ATTR(enable, 0444, enable_show, NULL);

Ditto.

> +
> +static ssize_t cur_lane_num_show(struct kobject *kobj,
> +				 struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +	struct hccs_dev *hdev = port->die->chip->hdev;
> +	struct hccs_link_status link_status = {0};
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_query_port_link_status(hdev, port, &link_status);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%u\n", link_status.lane_num);
> +}
> +
> +static struct kobj_attribute cur_lane_num_attr =
> +			__ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
> +
> +static ssize_t link_fsm_show(struct kobject *kobj,
> +			     struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +	struct hccs_dev *hdev = port->die->chip->hdev;
> +	struct hccs_link_status link_status = {0};
> +	const struct {
> +		u8 link_fsm;
> +		char *str;
> +	} link_fsm_map[] = {
> +		{HCCS_PORT_RESET, "reset"},
> +		{HCCS_PORT_SETUP, "setup"},
> +		{HCCS_PORT_CONFIG, "config"},
> +		{HCCS_PORT_READY, "link-up"},
> +	};
> +	const char *link_fsm_str = "unknown";
> +	size_t i;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_query_port_link_status(hdev, port, &link_status);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
> +		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
> +			link_fsm_str = link_fsm_map[i].str;
> +			break;
> +		}
> +	}
> +
> +	return sysfs_emit(buf, "%s\n", link_fsm_str);
> +}
> +
> +static struct kobj_attribute link_fsm_attr =
> +			__ATTR(link_fsm, 0444, link_fsm_show, NULL);
> +
> +static ssize_t lane_mask_show(struct kobject *kobj,
> +			      struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +	struct hccs_dev *hdev = port->die->chip->hdev;
> +	struct hccs_link_status link_status = {0};
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_query_port_link_status(hdev, port, &link_status);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
> +}
> +
> +static struct kobj_attribute lane_mask_attr =
> +			__ATTR(lane_mask, 0444, lane_mask_show, NULL);
> +
> +static ssize_t crc_err_cnt_show(struct kobject *kobj,
> +				struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
> +	struct hccs_dev *hdev = port->die->chip->hdev;
> +	u64 crc_err_cnt;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
> +}
> +
> +static struct kobj_attribute crc_err_cnt_attr =
> +			__ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
> +
> +static struct attribute *hccs_port_default_attrs[] = {
> +	&hccs_type_attr.attr,
> +	&lane_mode_attr.attr,
> +	&port_enable_attr.attr,
> +	&cur_lane_num_attr.attr,
> +	&link_fsm_attr.attr,
> +	&lane_mask_attr.attr,
> +	&crc_err_cnt_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(hccs_port_default);
> +
> +static const struct kobj_type hccs_port_type = {
> +	.sysfs_ops = &hccs_comm_ops,
> +	.default_groups = hccs_port_default_groups,
> +};
> +
> +static ssize_t all_linked_on_die_show(struct kobject *kobj,
> +				      struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_die_info *die = kobj_to_die_info(kobj);
> +	struct hccs_dev *hdev = die->chip->hdev;
> +	u8 all_linked;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%u\n", all_linked);
> +}
> +static struct kobj_attribute all_linked_on_die_attr =
> +		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
> +
> +static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
> +					    struct kobj_attribute *attr,
> +					    char *buf)
> +{
> +	const struct hccs_die_info *die = kobj_to_die_info(kobj);
> +	struct hccs_dev *hdev = die->chip->hdev;
> +	u8 full_lane;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%u\n", full_lane);
> +}
> +static struct kobj_attribute linked_full_lane_on_die_attr =
> +	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
> +
> +static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
> +					   struct kobj_attribute *attr,
> +					   char *buf)
> +{
> +	const struct hccs_die_info *die = kobj_to_die_info(kobj);
> +	struct hccs_dev *hdev = die->chip->hdev;
> +	u64 total_crc_err_cnt;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
> +	mutex_unlock(&hdev->lock);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
> +}
> +static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
> +	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
> +
> +static struct attribute *hccs_die_default_attrs[] = {
> +	&all_linked_on_die_attr.attr,
> +	&linked_full_lane_on_die_attr.attr,
> +	&crc_err_cnt_sum_on_die_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(hccs_die_default);
> +
> +static const struct kobj_type hccs_die_type = {
> +	.sysfs_ops = &hccs_comm_ops,
> +	.default_groups = hccs_die_default_groups,
> +};
> +
> +static ssize_t all_linked_on_chip_show(struct kobject *kobj,
> +				       struct kobj_attribute *attr, char *buf)
> +{
> +	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
> +	struct hccs_dev *hdev = chip->hdev;
> +	const struct hccs_die_info *die;
> +	u8 all_linked = 1;
> +	u8 i, tmp;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	for (i = 0; i < chip->die_num; i++) {
> +		die = &chip->dies[i];
> +		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
> +		if (ret) {
> +			mutex_unlock(&hdev->lock);
> +			return ret;
> +		}
> +		if (tmp != all_linked) {
> +			all_linked = 0;
> +			break;
> +		}
> +	}
> +	mutex_unlock(&hdev->lock);
> +
> +	return sysfs_emit(buf, "%u\n", all_linked);
> +}
> +static struct kobj_attribute all_linked_on_chip_attr =
> +		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
> +
> +static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
> +					     struct kobj_attribute *attr,
> +					     char *buf)
> +{
> +	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
> +	struct hccs_dev *hdev = chip->hdev;
> +	const struct hccs_die_info *die;
> +	u8 full_lane = 1;
> +	u8 i, tmp;
> +	int ret;
> +
> +	mutex_lock(&hdev->lock);
> +	for (i = 0; i < chip->die_num; i++) {
> +		die = &chip->dies[i];
> +		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
> +		if (ret) {
> +			mutex_unlock(&hdev->lock);
> +			return ret;
> +		}
> +		if (tmp != full_lane) {
> +			full_lane = 0;
> +			break;
> +		}
> +	}
> +	mutex_unlock(&hdev->lock);
> +
> +	return sysfs_emit(buf, "%u\n", full_lane);
> +}
> +static struct kobj_attribute linked_full_lane_on_chip_attr =
> +	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
> +
> +static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
> +					    struct kobj_attribute *attr,
> +					    char *buf)
> +{
> +	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
> +	u64 crc_err_cnt, total_crc_err_cnt = 0;
> +	struct hccs_dev *hdev = chip->hdev;
> +	const struct hccs_die_info *die;
> +	int ret;
> +	u16 i;
> +
> +	mutex_lock(&hdev->lock);
> +	for (i = 0; i < chip->die_num; i++) {
> +		die = &chip->dies[i];
> +		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
> +		if (ret) {
> +			mutex_unlock(&hdev->lock);
> +			return ret;
> +		}
> +
> +		total_crc_err_cnt += crc_err_cnt;
> +	}
> +	mutex_unlock(&hdev->lock);
> +
> +	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
> +}
> +static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
> +		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
> +
> +static struct attribute *hccs_chip_default_attrs[] = {
> +	&all_linked_on_chip_attr.attr,
> +	&linked_full_lane_on_chip_attr.attr,
> +	&crc_err_cnt_sum_on_chip_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(hccs_chip_default);
> +
> +static const struct kobj_type hccs_chip_type = {
> +	.sysfs_ops = &hccs_comm_ops,
> +	.default_groups = hccs_chip_default_groups,
> +};
> +
> +static void hccs_remove_die_dir(struct hccs_die_info *die)
> +{
> +	struct hccs_port_info *port;
> +	u8 i;
> +
> +	for (i = 0; i < die->port_num; i++) {
> +		port = &die->ports[i];
> +		if (port->dir_created)
> +			kobject_put(&port->kobj);
> +	}
> +
> +	kobject_put(&die->kobj);
> +}
> +
> +static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
> +{
> +	struct hccs_die_info *die;
> +	u8 i;
> +
> +	for (i = 0; i < chip->die_num; i++) {
> +		die = &chip->dies[i];
> +		if (die->dir_created)
> +			hccs_remove_die_dir(die);
> +	}
> +
> +	kobject_put(&chip->kobj);
> +}
> +
> +static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
> +{
> +	u8 i;
> +
> +	for (i = 0; i < hdev->chip_num; i++)
> +		hccs_remove_chip_dir(&hdev->chips[i]);
> +}
> +
> +static int hccs_create_hccs_dir(struct hccs_dev *hdev,
> +				struct hccs_die_info *die,
> +				struct hccs_port_info *port)
> +{
> +	int ret;
> +
> +	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
> +				   &die->kobj, "hccs%d", port->port_id);
> +	if (ret) {
> +		kobject_put(&port->kobj);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hccs_create_die_dir(struct hccs_dev *hdev,
> +			       struct hccs_chip_info *chip,
> +			       struct hccs_die_info *die)
> +{
> +	struct hccs_port_info *port;
> +	int ret;
> +	u16 i;
> +
> +	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
> +				   &chip->kobj, "die%d", die->die_id);
> +	if (ret) {
> +		kobject_put(&die->kobj);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < die->port_num; i++) {
> +		port = &die->ports[i];
> +		ret = hccs_create_hccs_dir(hdev, die, port);
> +		if (ret) {
> +			dev_err(hdev->dev, "create hccs%d dir failed.\n",
> +				port->port_id);
> +			goto err;
> +		}
> +		port->dir_created = true;
> +	}
> +
> +	return 0;
> +err:
> +	hccs_remove_die_dir(die);
> +
> +	return ret;
> +}
> +
> +static int hccs_create_chip_dir(struct hccs_dev *hdev,
> +				struct hccs_chip_info *chip)
> +{
> +	struct hccs_die_info *die;
> +	int ret;
> +	u16 id;
> +
> +	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
> +				   &hdev->dev->kobj, "chip%d", chip->chip_id);
> +	if (ret) {
> +		kobject_put(&chip->kobj);
> +		return ret;
> +	}
> +
> +	for (id = 0; id < chip->die_num; id++) {
> +		die = &chip->dies[id];
> +		ret = hccs_create_die_dir(hdev, chip, die);
> +		if (ret)
> +			goto err;
> +		die->dir_created = true;
> +	}
> +
> +	return 0;
> +err:
> +	hccs_remove_chip_dir(chip);
> +
> +	return ret;
> +}
> +
> +static int hccs_create_topo_dirs(struct hccs_dev *hdev)
> +{
> +	struct hccs_chip_info *chip;
> +	u8 id, k;
> +	int ret;
> +
> +	for (id = 0; id < hdev->chip_num; id++) {
> +		chip = &hdev->chips[id];
> +		ret = hccs_create_chip_dir(hdev, chip);
> +		if (ret) {
> +			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +err:
> +	for (k = 0; k < id; k++)
> +		hccs_remove_chip_dir(&hdev->chips[k]);
> +
> +	return ret;
> +}
> +
> +static int hccs_probe(struct platform_device *pdev)
> +{
> +	struct acpi_device *acpi_dev;
> +	struct hccs_dev *hdev;
> +	int rc;
> +
> +	if (acpi_disabled) {
> +		dev_err(&pdev->dev, "acpi is disabled.\n");
> +		return -ENODEV;
> +	}
> +	acpi_dev = ACPI_COMPANION(&pdev->dev);
> +	if (!acpi_dev)
> +		return -ENODEV;
> +
> +	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
> +	if (!hdev)
> +		return -ENOMEM;
> +	hdev->acpi_dev = acpi_dev;
> +	hdev->dev = &pdev->dev;
> +	platform_set_drvdata(pdev, hdev);
> +
> +	mutex_init(&hdev->lock);
> +	rc = hccs_get_pcc_chan_id(hdev);
> +	if (rc)
> +		return rc;
> +	rc = hccs_register_pcc_channel(hdev);
> +	if (rc)
> +		return rc;
> +
> +	rc = hccs_get_dev_caps(hdev);
> +	if (rc)
> +		goto unregister_pcc_chan;
> +
> +	rc = hccs_get_hw_info(hdev);
> +	if (rc)
> +		goto unregister_pcc_chan;
> +
> +	rc = hccs_create_topo_dirs(hdev);
> +	if (rc)
> +		goto unregister_pcc_chan;
> +
> +	return 0;
> +
> +unregister_pcc_chan:
> +	hccs_unregister_pcc_channel(hdev);
> +
> +	return rc;
> +}
> +
> +static int hccs_remove(struct platform_device *pdev)
> +{
> +	struct hccs_dev *hdev = platform_get_drvdata(pdev);
> +
> +	hccs_remove_topo_dirs(hdev);
> +	hccs_unregister_pcc_channel(hdev);
> +
> +	return 0;
> +}
> +
> +static const struct acpi_device_id hccs_acpi_match[] = {
> +	{ "HISI04B1", },
> +};

It is better to add MODULE_DEVICE_TABLE to autoload the driver.

Best Regards,
Wei

> +
> +static struct platform_driver hccs_driver = {
> +	.probe = hccs_probe,
> +	.remove = hccs_remove,
> +	.driver = {
> +		.name = "kunpeng_hccs",
> +		.acpi_match_table = hccs_acpi_match,
> +	},
> +};
> +
> +module_platform_driver(hccs_driver);
> +
> +MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
> new file mode 100644
> index 000000000000..f9a79904d9c7
> --- /dev/null
> +++ b/drivers/soc/hisilicon/kunpeng_hccs.h
> @@ -0,0 +1,196 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/* Copyright (c) 2023 Hisilicon Limited. */
> +
> +#ifndef __KUNPENG_HCCS_H__
> +#define __KUNPENG_HCCS_H__
> +
> +/*
> + * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
> + * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
> + * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
> + */
> +
> +/*
> + * This value cannot be 255, otherwise the loop of the multi-BD communication
> + * case cannot end.
> + */
> +#define HCCS_DIE_MAX_PORT_ID	254
> +
> +struct hccs_port_info {
> +	u8 port_id;
> +	u8 port_type;
> +	u8 lane_mode;
> +	bool enable; /* if the port is enabled */
> +	struct kobject kobj;
> +	bool dir_created;
> +	struct hccs_die_info *die; /* point to the die the port is located */
> +};
> +
> +struct hccs_die_info {
> +	u8 die_id;
> +	u8 port_num;
> +	u8 min_port_id;
> +	u8 max_port_id;
> +	struct hccs_port_info *ports;
> +	struct kobject kobj;
> +	bool dir_created;
> +	struct hccs_chip_info *chip; /* point to the chip the die is located */
> +};
> +
> +struct hccs_chip_info {
> +	u8 chip_id;
> +	u8 die_num;
> +	struct hccs_die_info *dies;
> +	struct kobject kobj;
> +	struct hccs_dev *hdev;
> +};
> +
> +struct hccs_mbox_client_info {
> +	struct mbox_client client;
> +	struct mbox_chan *mbox_chan;
> +	struct pcc_mbox_chan *pcc_chan;
> +	u64 deadline_us;
> +	void *pcc_comm_addr;
> +};
> +
> +struct hccs_dev {
> +	struct device *dev;
> +	struct acpi_device *acpi_dev;
> +	u64 caps;
> +	u8 chip_num;
> +	struct hccs_chip_info *chips;
> +	u8 chan_id;
> +	struct mutex lock;
> +	struct hccs_mbox_client_info cl_info;
> +};
> +
> +#define HCCS_SERDES_MODULE_CODE 0x32
> +enum hccs_subcmd_type {
> +	HCCS_GET_CHIP_NUM = 0x1,
> +	HCCS_GET_DIE_NUM,
> +	HCCS_GET_DIE_INFO,
> +	HCCS_GET_DIE_PORT_INFO,
> +	HCCS_GET_DEV_CAP,
> +	HCCS_GET_PORT_LINK_STATUS,
> +	HCCS_GET_PORT_CRC_ERR_CNT,
> +	HCCS_GET_DIE_PORTS_LANE_STA,
> +	HCCS_GET_DIE_PORTS_LINK_STA,
> +	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
> +	HCCS_SUB_CMD_MAX = 255,
> +};
> +
> +struct hccs_die_num_req_param {
> +	u8 chip_id;
> +};
> +
> +struct hccs_die_info_req_param {
> +	u8 chip_id;
> +	u8 die_idx;
> +};
> +
> +struct hccs_die_info_rsp_data {
> +	u8 die_id;
> +	u8 port_num;
> +	u8 min_port_id;
> +	u8 max_port_id;
> +};
> +
> +struct hccs_port_attr {
> +	u8 port_id;
> +	u8 port_type;
> +	u8 lane_mode;
> +	u8 enable : 1; /* if the port is enabled */
> +	u16 rsv[2];
> +};
> +
> +/*
> + * The common command request for getting the all information of HCCS port on
> + * specified DIE.
> + */
> +struct hccs_die_comm_req_param {
> +	u8 chip_id;
> +	u8 die_id; /* id in hardware */
> +};
> +
> +/* The common command request for getting the information of a specific port */
> +struct hccs_port_comm_req_param {
> +	u8 chip_id;
> +	u8 die_id;
> +	u8 port_id;
> +};
> +
> +#define HCCS_PORT_RESET         1
> +#define HCCS_PORT_SETUP         2
> +#define HCCS_PORT_CONFIG        3
> +#define HCCS_PORT_READY         4
> +struct hccs_link_status {
> +	u8 lane_mask; /* indicate which lanes are used. */
> +	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
> +	u8 lane_num : 5; /* current lane number */
> +};
> +
> +struct hccs_req_head {
> +	u8 module_code; /* set to 0x32 for serdes */
> +	u8 start_id;
> +	u8 rsv[2];
> +};
> +
> +struct hccs_rsp_head {
> +	u8 data_len;
> +	u8 next_id;
> +	u8 rsv[2];
> +};
> +
> +struct hccs_fw_inner_head {
> +	u8 retStatus; /* 0: success, other: failure */
> +	u8 rsv[7];
> +};
> +
> +#define HCCS_PCC_SHARE_MEM_BYTES	64
> +#define HCCS_FW_INNER_HEAD_BYTES	8
> +#define HCCS_RSP_HEAD_BYTES		4
> +
> +#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
> +					 HCCS_FW_INNER_HEAD_BYTES - \
> +					 HCCS_RSP_HEAD_BYTES)
> +#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
> +
> +/*
> + * Note: Actual available size of data field also depands on the PCC header
> + * bytes of the specific type. Driver needs to copy the response data in the
> + * communication space based on the real length.
> + */
> +struct hccs_rsp_desc {
> +	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
> +	struct hccs_rsp_head rsp_head; /* 4 Bytes */
> +	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
> +};
> +
> +#define HCCS_REQ_HEAD_BYTES		4
> +#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
> +					 HCCS_REQ_HEAD_BYTES)
> +#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
> +
> +/*
> + * Note: Actual available size of data field also depands on the PCC header
> + * bytes of the specific type. Driver needs to copy the request data to the
> + * communication space based on the real length.
> + */
> +struct hccs_req_desc {
> +	struct hccs_req_head req_head; /* 4 Bytes */
> +	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
> +};
> +
> +struct hccs_desc {
> +	union {
> +		struct hccs_req_desc req;
> +		struct hccs_rsp_desc rsp;
> +	};
> +};
> +
> +#define hccs_get_field(origin, mask, shift) \
> +	(((origin) & (mask)) >> (shift))
> +#define hccs_get_bit(origin, shift) \
> +	hccs_get_field((origin), (0x1UL << (shift)), (shift))
> +
> +#endif /* __KUNPENG_HCCS_H__ */
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-07-25  7:57   ` [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
@ 2023-07-25  8:59     ` Wei Xu
  2023-07-26  9:56       ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Wei Xu @ 2023-07-25  8:59 UTC (permalink / raw)
  To: Huisong Li, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong

Hi Huisong,

On 2023/7/25 15:57, Huisong Li wrote:
> Document the sysfs attributes description provided by HCCS driver on
> Kunpeng SoC.
> 
> Signed-off-by: Huisong Li <lihuisong@huawei.com>
> ---
>  .../sysfs-devices-platform-kunpeng_hccs       | 76 +++++++++++++++++++
>  MAINTAINERS                                   |  1 +
>  2 files changed, 77 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
> 
> diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
> new file mode 100644
> index 000000000000..83ebed801249
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
> @@ -0,0 +1,76 @@
> +What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
> +		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
> +		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
> +Date:		May 2023

Please update the date to "November 2023" to match the 6.6 kernel.

> +KernelVersion:	6.6
> +Contact:	Huisong Li <lihuisong@huawei.org>
> +Description:
> +		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
> +		contains read-only attributes exposing some summarization
> +		information of all HCCS ports under a specified chip.
> +		The X in 'chipX' indicates the Xth chip on platform.
> +
> +		There are following attributes in this directory:
> +		================= ==== =========================================
> +		all_linked:       (RO) if all enabled ports on this chip are
> +				       linked (bool).
> +		linked_full_lane: (RO) if all linked ports on this chip are full
> +				       lane (bool).
> +		crc_err_cnt:      (RO) total CRC err count for all ports on this
> +				       chip.
> +		============= ==== =============================================
> +
> +What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
> +Date:		May 2023

Ditto.

> +KernelVersion:	6.6
> +Contact:	Huisong Li <lihuisong@huawei.org>
> +Description:
> +		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
> +		contains read-only attributes exposing some summarization
> +		information of all HCCS ports under a specified die.
> +		The Y in 'dieY' indicates the hardware id of the die on chip who
> +		has chip id X.
> +
> +		There are following attributes in this directory:
> +		================= ==== =========================================
> +		all_linked:       (RO) if all enabled ports on this die are
> +				       linked (bool).
> +		linked_full_lane: (RO) if all linked ports on this die are full
> +				       lane (bool).
> +		crc_err_cnt:      (RO) total CRC err count for all ports on this
> +				       die.
> +		============= ==== =============================================
> +
> +What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
> +Date:		May 2023

Ditto.

Best Regards,
Wei

> +KernelVersion:	6.6
> +Contact:	Huisong Li <lihuisong@huawei.org>
> +Description:
> +		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
> +		contains read-only attributes exposing information about
> +		a HCCS port. The N value in 'hccsN' indicates this port id.
> +		The X in 'chipX' indicates the ID of the chip to which the
> +		HCCS port belongs. For example, X ranges from to 'n - 1' if the
> +		chip number on platform is n.
> +		The Y in 'dieY' indicates the hardware id of the die to which
> +		the hccs port belongs.
> +
> +		The HCCS port have the following attributes:
> +		============= ==== =============================================
> +		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
> +		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
> +		enable:       (RO) indicate if this port is enabled (bool).
> +		cur_lane_num: (RO) current lane number of this port.
> +		lane_mask:    (RO) current lane mask of this port, every bit
> +			           indicates a lane.
> +		crc_err_cnt:  (RO) CRC err count on this port.
> +		============= ==== =============================================
> +		Note: type, lane_mode and enable are fixed attributes on
> +		      running platform.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 4e55ff992171..7a34bab232eb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9474,6 +9474,7 @@ F:	drivers/crypto/hisilicon/zip/
>  HISILICON KUNPENG SOC HCCS DRIVER
>  M:	Huisong Li <lihuisong@huawei.com>
>  S:	Maintained
> +F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>  F:	drivers/soc/hisilicon/kunpeng_hccs.c
>  F:	drivers/soc/hisilicon/kunpeng_hccs.h
>  
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-25  7:57   ` [PATCH RESEND v3 1/2] " Huisong Li
  2023-07-25  8:55     ` Wei Xu
@ 2023-07-25 15:28     ` Randy Dunlap
  2023-07-26  9:48       ` lihuisong (C)
  1 sibling, 1 reply; 79+ messages in thread
From: Randy Dunlap @ 2023-07-25 15:28 UTC (permalink / raw)
  To: Huisong Li, xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong

Hi--

On 7/25/23 00:57, Huisong Li wrote:
> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
> new file mode 100644
> index 000000000000..87a1f15cbedb
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Kconfig
> @@ -0,0 +1,19 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Hisilicon SoC drivers"
> +	depends on ARCH_HISI || COMPILE_TEST
> +
> +config KUNPENG_HCCS
> +	tristate "HCCS driver on Kunpeng SoC"
> +	depends on ACPI
> +	depends on ARM64 || COMPILE_TEST
> +	help
> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard

Just curious: what makes it a standard?

> +	  for ensuring cache coherent on HiSilicon SoC. The performance of

	                     coherence

> +	  the application may be affected if some hccs ports are in non-full

	                                          HCCS

> +	  lane status, have a large number of CRC errors and so on.
> +
> +	  Say M here if you want to include support for querying the health
> +	  status and port information of HCCS on Kunpeng SoC.
> +
> +endmenu

thanks.
-- 
~Randy

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-25 15:28     ` Randy Dunlap
@ 2023-07-26  9:48       ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-07-26  9:48 UTC (permalink / raw)
  To: Randy Dunlap, xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Hi Randy Dunlap,

many thanks for reviewing my patch.


在 2023/7/25 23:28, Randy Dunlap 写道:
> Hi--
>
> On 7/25/23 00:57, Huisong Li wrote:
>> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..87a1f15cbedb
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,19 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +
>> +config KUNPENG_HCCS
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ACPI
>> +	depends on ARM64 || COMPILE_TEST
>> +	help
>> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
> Just curious: what makes it a standard?
HCCS is just a self-defined bus protocol and used for multi-chip 
interconnection.
The description above doesn't seem appropriate.
I will fix these releated description in next version.
>
>> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
> 	                     coherence
Ack, thanks.
>
>> +	  the application may be affected if some hccs ports are in non-full
> 	                                          HCCS
HCCS is a bus protocol, it should be all capitals.
And hccs port is an abstraction object in code layer here.
So maybe it is ok here. what do you think?

Best regards,
Huisong

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-25  8:55     ` Wei Xu
@ 2023-07-26  9:54       ` lihuisong (C)
  2023-07-27  3:51         ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: lihuisong (C) @ 2023-07-26  9:54 UTC (permalink / raw)
  To: Wei Xu, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Hi Wei,

Thanks for your review.


在 2023/7/25 16:55, Wei Xu 写道:
> Hi Huisong,
>
> On 2023/7/25 15:57, Huisong Li wrote:
>> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> for ensuring cache coherent on HiSilicon SoC. The performance of
>> the application may be affected if some hccs ports are in non-full
>> lane status, have a large number of CRC errors and so on.
>>
>> This driver provides the query interface of the health status and
>> port information of HCCS on Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>> ---
>>   MAINTAINERS                          |    6 +
>>   drivers/soc/Kconfig                  |    1 +
>>   drivers/soc/Makefile                 |    1 +
>>   drivers/soc/hisilicon/Kconfig        |   19 +
>>   drivers/soc/hisilicon/Makefile       |    2 +
>>   drivers/soc/hisilicon/kunpeng_hccs.c | 1288 ++++++++++++++++++++++++++
>>   drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>>   7 files changed, 1513 insertions(+)
>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index a5c16bb92fe2..4e55ff992171 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9471,6 +9471,12 @@ S:	Maintained
>>   F:	Documentation/ABI/testing/debugfs-hisi-zip
>>   F:	drivers/crypto/hisilicon/zip/
>>   
>> +HISILICON KUNPENG SOC HCCS DRIVER
>> +M:	Huisong Li <lihuisong@huawei.com>
>> +S:	Maintained
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
>> +
>>   HMM - Heterogeneous Memory Management
>>   M:	Jérôme Glisse <jglisse@redhat.com>
>>   L:	linux-mm@kvack.org
>> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
>> index 4e176280113a..d21e75d69294 100644
>> --- a/drivers/soc/Kconfig
>> +++ b/drivers/soc/Kconfig
>> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>>   source "drivers/soc/canaan/Kconfig"
>>   source "drivers/soc/fsl/Kconfig"
>>   source "drivers/soc/fujitsu/Kconfig"
>> +source "drivers/soc/hisilicon/Kconfig"
>>   source "drivers/soc/imx/Kconfig"
>>   source "drivers/soc/ixp4xx/Kconfig"
>>   source "drivers/soc/litex/Kconfig"
>> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
>> index 3b0f9fb3b5c8..531f46f3ad98 100644
>> --- a/drivers/soc/Makefile
>> +++ b/drivers/soc/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
>>   obj-y				+= fsl/
>>   obj-y				+= fujitsu/
>>   obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
>> +obj-y				+= hisilicon/
>>   obj-y				+= imx/
>>   obj-y				+= ixp4xx/
>>   obj-$(CONFIG_SOC_XWAY)		+= lantiq/
>> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..87a1f15cbedb
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,19 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +
>> +config KUNPENG_HCCS
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ACPI
>> +	depends on ARM64 || COMPILE_TEST
>> +	help
>> +	  The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>> +	  for ensuring cache coherent on HiSilicon SoC. The performance of
>> +	  the application may be affected if some hccs ports are in non-full
>> +	  lane status, have a large number of CRC errors and so on.
>> +
>> +	  Say M here if you want to include support for querying the health
>> +	  status and port information of HCCS on Kunpeng SoC.
>> +
>> +endmenu
>> diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
>> new file mode 100644
>> index 000000000000..226e747e70d6
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Makefile
>> @@ -0,0 +1,2 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
>> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
>> new file mode 100644
>> index 000000000000..d7f4bb04deb3
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
>> @@ -0,0 +1,1288 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol standard for
>> + * ensuring cache coherent on HiSilicon SoC.
>> + *
>> + * Copyright (c) 2023 Hisilicon Limited.
>> + * Author: Huisong Li <lihuisong@huawei.com>
>> + *
>> + * HCCS driver for Kunpeng SoC provides the following features:
>> + * - Retrieve the following information about each port:
>> + *    - port type
>> + *    - lane mode
>> + *    - enable
>> + *    - current lane mode
>> + *    - link state machine
>> + *    - lane mask
>> + *    - CRC error count
>> + *
>> + * - Retrieve the following information about all the ports on the chip or
>> + *   the die:
>> + *    - if all enabled ports are in linked
>> + *    - if all linked ports are in full lane
>> + *    - CRC error count sum
>> + */
>> +#include <linux/sysfs.h>
>> +#include <linux/acpi.h>
>> +#include <linux/io.h>
>> +#include <linux/kobject.h>
> Please remove the useless including of the io.h and kobject.h.
useless header, indeed. will remove in v4.
>
>> +#include <linux/iopoll.h>
>> +#include <linux/platform_device.h>
> It is better to put all the linux headers into alphabetical order.
Ack
>
>> +#include <acpi/pcc.h>
>> +
>> +#include "kunpeng_hccs.h"
>> +
>> +/* PCC defines */
>> +#define HCCS_PCC_SIGNATURE_MASK		0x50434300
>> +#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
>> +
>> +/*
>> + * Arbitrary retries in case the remote processor is slow to respond
>> + * to PCC commands
>> + */
>> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
>> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
>> +
>> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_port_info, kobj);
>> +}
>> +
>> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_die_info, kobj);
>> +}
>> +
>> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
>> +{
>> +	return container_of(k, struct hccs_chip_info, kobj);
>> +}
>> +
>> +struct hccs_register_ctx {
>> +	struct device *dev;
>> +	u8 chan_id;
>> +	int err;
>> +};
>> +
>> +static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
>> +					void *context)
>> +{
>> +	struct acpi_resource_generic_register *reg;
>> +	struct hccs_register_ctx *ctx = context;
>> +
>> +	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
>> +		return AE_OK;
>> +
>> +	reg = &ares->data.generic_reg;
>> +	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
>> +		dev_err(ctx->dev, "Bad register resource.\n");
>> +		ctx->err = -EINVAL;
>> +		return AE_ERROR;
>> +	}
>> +	ctx->chan_id = reg->access_size;
>> +
>> +	return AE_OK;
>> +}
>> +
>> +static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
>> +{
>> +	acpi_handle handle = ACPI_HANDLE(hdev->dev);
>> +	struct hccs_register_ctx ctx = {0};
>> +	acpi_status status;
>> +
>> +	if (!acpi_has_method(handle, METHOD_NAME__CRS))
>> +		return -ENODEV;
>> +
>> +	ctx.dev = hdev->dev;
>> +	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
>> +				     hccs_get_register_cb, &ctx);
>> +	if (ACPI_FAILURE(status))
>> +		return ctx.err;
>> +	hdev->chan_id = ctx.chan_id;
>> +
>> +	return 0;
>> +}
>> +
>> +static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
>> +{
>> +	if (ret < 0)
>> +		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
>> +			 *(u8 *)msg, ret);
>> +	else
>> +		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
>> +			 *(u8 *)msg, ret);
>> +}
>> +
>> +static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +
>> +	if (cl_info->pcc_comm_addr)
>> +		iounmap(cl_info->pcc_comm_addr);
>> +	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
>> +}
>> +
>> +static int hccs_register_pcc_channel(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct mbox_client *cl = &cl_info->client;
>> +	struct pcc_mbox_chan *pcc_chan;
>> +	struct device *dev = hdev->dev;
>> +	int rc;
>> +
>> +	cl->dev = dev;
>> +	cl->tx_block = false;
>> +	cl->knows_txdone = true;
>> +	cl->tx_done = hccs_chan_tx_done;
>> +	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
>> +	if (IS_ERR(pcc_chan)) {
>> +		dev_err(dev, "PPC channel request failed.\n");
>> +		rc = -ENODEV;
>> +		goto out;
>> +	}
>> +	cl_info->pcc_chan = pcc_chan;
>> +	cl_info->mbox_chan = pcc_chan->mchan;
>> +
>> +	/*
>> +	 * pcc_chan->latency is just a nominal value. In reality the remote
>> +	 * processor could be much slower to reply. So add an arbitrary amount
>> +	 * of wait on top of nominal.
>> +	 */
>> +	cl_info->deadline_us =
>> +			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
>> +	if (cl_info->mbox_chan->mbox->txdone_irq) {
>> +		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
>> +		rc = -EINVAL;
>> +		goto err_mbx_channel_free;
>> +	}
>> +
>> +	if (pcc_chan->shmem_base_addr) {
>> +		cl_info->pcc_comm_addr = (void __force *)ioremap(
>> +			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
>> +		if (!cl_info->pcc_comm_addr) {
>> +			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
>> +				hdev->chan_id);
>> +			rc = -ENOMEM;
>> +			goto err_mbx_channel_free;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +err_mbx_channel_free:
>> +	pcc_mbox_free_channel(cl_info->pcc_chan);
>> +out:
>> +	return rc;
>> +}
>> +
>> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
>> +	u16 status;
>> +	int ret;
>> +
>> +	/*
>> +	 * Poll PCC status register every 3us(delay_us) for maximum of
>> +	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
>> +	 */
>> +	ret = readw_poll_timeout(&comm_base->status, status,
>> +				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
>> +				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
>> +				 cl_info->deadline_us);
>> +	if (unlikely(ret))
>> +		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
>> +			     struct hccs_desc *desc)
>> +{
>> +	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>> +	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
>> +	void *comm_space = (void *)(comm_base + 1);
>> +	struct hccs_fw_inner_head *fw_inner_head;
>> +	struct acpi_pcct_shared_memory tmp = {0};
>> +	u16 comm_space_size;
>> +	int ret;
>> +
>> +	/* Write signature for this subspace */
>> +	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
>> +	/* Write to the shared command region */
>> +	tmp.command = cmd;
>> +	/* Clear cmd complete bit */
>> +	tmp.status = 0;
>> +	memcpy_toio(comm_base, (void *)&tmp,
>> +			sizeof(struct acpi_pcct_shared_memory));
>> +
>> +	/* Copy the message to the PCC comm space */
>> +	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
>> +				sizeof(struct acpi_pcct_shared_memory);
>> +	memcpy_toio(comm_space, (void *)desc, comm_space_size);
>> +
>> +	/* Ring doorbell */
>> +	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
>> +	if (ret < 0) {
>> +		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
>> +			ret);
>> +		goto end;
>> +	}
>> +
>> +	/* Wait for completion */
>> +	ret = hccs_check_chan_cmd_complete(hdev);
>> +	if (ret)
>> +		goto end;
>> +
>> +	/* Copy response data */
>> +	memcpy_fromio((void *)desc, comm_space, comm_space_size);
>> +	fw_inner_head = &desc->rsp.fw_inner_head;
>> +	if (fw_inner_head->retStatus) {
>> +		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
>> +			fw_inner_head->retStatus);
>> +		ret = -EIO;
>> +	}
>> +
>> +end:
>> +	mbox_client_txdone(cl_info->mbox_chan, ret);
>> +	return ret;
>> +}
>> +
>> +static void hccs_init_req_desc(struct hccs_desc *desc)
>> +{
>> +	struct hccs_req_desc *req = &desc->req;
>> +
>> +	memset(desc, 0, sizeof(*desc));
>> +	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
>> +}
>> +
>> +static int hccs_get_dev_caps(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	hccs_init_req_desc(&desc);
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	hccs_init_req_desc(&desc);
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	hdev->chip_num = *((u8 *)&desc.rsp.data);
>> +	if (!hdev->chip_num) {
>> +		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_chip_info(struct hccs_dev *hdev,
>> +			      struct hccs_chip_info *chip)
>> +{
>> +	struct hccs_die_num_req_param *req_param;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_die_num_req_param *)desc.req.data;
>> +	req_param->chip_id = chip->chip_id;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	chip->die_num = *((u8 *)&desc.rsp.data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_chip_info *chip;
>> +	int ret;
>> +	u8 idx;
>> +
>> +	ret = hccs_query_chip_num_on_platform(hdev);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	hdev->chips = devm_kzalloc(hdev->dev,
>> +				hdev->chip_num * sizeof(struct hccs_chip_info),
>> +				GFP_KERNEL);
>> +	if (!hdev->chips) {
>> +		dev_err(hdev->dev, "allocate all chips memory failed.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	for (idx = 0; idx < hdev->chip_num; idx++) {
>> +		chip = &hdev->chips[idx];
>> +		chip->chip_id = idx;
>> +		ret = hccs_get_chip_info(hdev, chip);
>> +		if (ret) {
>> +			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
>> +				idx, ret);
>> +			return ret;
>> +		}
>> +		chip->hdev = hdev;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
>> +				       u8 die_idx, struct hccs_die_info *die)
>> +{
>> +	struct hccs_die_info_req_param *req_param;
>> +	struct hccs_die_info_rsp_data *rsp_data;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_die_info_req_param *)desc.req.data;
>> +	req_param->chip_id = chip_id;
>> +	req_param->die_idx = die_idx;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
>> +	die->die_id = rsp_data->die_id;
>> +	die->port_num = rsp_data->port_num;
>> +	die->min_port_id = rsp_data->min_port_id;
>> +	die->max_port_id = rsp_data->max_port_id;
>> +	if (die->min_port_id > die->max_port_id) {
>> +		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
>> +			die->min_port_id, die->max_port_id, die_idx);
>> +		return -EINVAL;
>> +	}
>> +	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
>> +		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
>> +			die->max_port_id, die_idx);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
>> +{
>> +	struct device *dev = hdev->dev;
>> +	struct hccs_chip_info *chip;
>> +	struct hccs_die_info *die;
>> +	u8 i, j;
>> +	int ret;
>> +
>> +	for (i = 0; i < hdev->chip_num; i++) {
>> +		chip = &hdev->chips[i];
>> +		if (!chip->die_num)
>> +			continue;
>> +
>> +		chip->dies = devm_kzalloc(hdev->dev,
>> +				chip->die_num * sizeof(struct hccs_die_info),
>> +				GFP_KERNEL);
>> +		if (!chip->dies) {
>> +			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
>> +				i);
>> +			return -ENOMEM;
>> +		}
>> +
>> +		for (j = 0; j < chip->die_num; j++) {
>> +			die = &chip->dies[j];
>> +			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
>> +			if (ret) {
>> +				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
>> +					j, i, ret);
>> +				return ret;
>> +			}
>> +			die->chip = chip;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
>> +			    struct hccs_desc *desc,
>> +			    void *buf, size_t buf_len,
>> +			    struct hccs_rsp_head *rsp_head)
>> +{
>> +	struct hccs_rsp_head *head;
>> +	struct hccs_rsp_desc *rsp;
>> +	int ret;
>> +
>> +	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	rsp = &desc->rsp;
>> +	head = &rsp->rsp_head;
>> +	if (head->data_len > buf_len) {
>> +		dev_err(hdev->dev,
>> +			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
>> +			buf_len, head->data_len);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	memcpy(buf, rsp->data, head->data_len);
>> +	*rsp_head = *head;
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_all_port_attr(struct hccs_dev *hdev,
>> +				  struct hccs_die_info *die,
>> +				  struct hccs_port_attr *attrs, u16 size)
>> +{
>> +	struct hccs_die_comm_req_param *req_param;
>> +	struct hccs_req_head *req_head;
>> +	struct hccs_rsp_head rsp_head;
>> +	struct hccs_desc desc;
>> +	size_t left_buf_len;
>> +	u32 data_len = 0;
>> +	u8 start_id;
>> +	u8 *buf;
>> +	int ret;
>> +
>> +	buf = (u8 *)attrs;
>> +	left_buf_len = sizeof(struct hccs_port_attr) * size;
>> +	start_id = die->min_port_id;
>> +	while (start_id <= die->max_port_id) {
>> +		hccs_init_req_desc(&desc);
>> +		req_head = &desc.req.req_head;
>> +		req_head->start_id = start_id;
>> +		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>> +		req_param->chip_id = die->chip->chip_id;
>> +		req_param->die_id = die->die_id;
>> +
>> +		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
>> +				       buf + data_len, left_buf_len, &rsp_head);
>> +		if (ret) {
>> +			dev_err(hdev->dev,
>> +				"get the information of port%u on die%u failed, ret = %d.\n",
>> +				start_id, die->die_id, ret);
>> +			return ret;
>> +		}
>> +
>> +		data_len += rsp_head.data_len;
>> +		left_buf_len -= rsp_head.data_len;
>> +		if (unlikely(rsp_head.next_id <= start_id)) {
>> +			dev_err(hdev->dev,
>> +				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
>> +				rsp_head.next_id, start_id, die->die_id);
>> +			return -EINVAL;
>> +		}
>> +		start_id = rsp_head.next_id;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
>> +					 struct hccs_die_info *die)
>> +{
>> +	struct hccs_port_attr *attrs;
>> +	struct hccs_port_info *port;
>> +	int ret;
>> +	u8 i;
>> +
>> +	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
>> +			GFP_KERNEL);
>> +	if (!attrs)
>> +		return -ENOMEM;
>> +
>> +	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
>> +	if (ret)
>> +		goto out;
>> +
>> +	for (i = 0; i < die->port_num; i++) {
>> +		port = &die->ports[i];
>> +		port->port_id = attrs[i].port_id;
>> +		port->port_type = attrs[i].port_type;
>> +		port->lane_mode = attrs[i].lane_mode;
>> +		port->enable = attrs[i].enable;
>> +		port->die = die;
>> +	}
>> +
>> +out:
>> +	kfree(attrs);
>> +	return ret;
>> +}
>> +
>> +static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
>> +{
>> +
>> +	struct device *dev = hdev->dev;
>> +	struct hccs_chip_info *chip;
>> +	struct hccs_die_info *die;
>> +	u8 i, j;
>> +	int ret;
>> +
>> +	for (i = 0; i < hdev->chip_num; i++) {
>> +		chip = &hdev->chips[i];
>> +		for (j = 0; j < chip->die_num; j++) {
>> +			die = &chip->dies[j];
>> +			if (!die->port_num)
>> +				continue;
>> +
>> +			die->ports = devm_kzalloc(dev,
>> +				die->port_num * sizeof(struct hccs_port_info),
>> +				GFP_KERNEL);
>> +			if (!die->ports) {
>> +				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
>> +					i, die->die_id);
>> +				return -ENOMEM;
>> +			}
>> +
>> +			ret = hccs_get_all_port_info_on_die(hdev, die);
>> +			if (ret) {
>> +				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
>> +					die->die_id, i, ret);
>> +				return ret;
>> +			}
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_hw_info(struct hccs_dev *hdev)
>> +{
>> +	int ret;
>> +
>> +	ret = hccs_query_chip_info_on_platform(hdev);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = hccs_query_all_die_info_on_platform(hdev);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = hccs_query_all_port_info_on_platform(hdev);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_query_port_link_status(struct hccs_dev *hdev,
>> +				       const struct hccs_port_info *port,
>> +				       struct hccs_link_status *link_status)
>> +{
>> +	const struct hccs_die_info *die = port->die;
>> +	const struct hccs_chip_info *chip = die->chip;
>> +	struct hccs_port_comm_req_param *req_param;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
>> +	req_param->chip_id = chip->chip_id;
>> +	req_param->die_id = die->die_id;
>> +	req_param->port_id = port->port_id;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev,
>> +			"get port link status info failed, ret = %d.\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	*link_status = *((struct hccs_link_status *)desc.rsp.data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
>> +				       const struct hccs_port_info *port,
>> +				       u64 *crc_err_cnt)
>> +{
>> +	const struct hccs_die_info *die = port->die;
>> +	const struct hccs_chip_info *chip = die->chip;
>> +	struct hccs_port_comm_req_param *req_param;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
>> +	req_param->chip_id = chip->chip_id;
>> +	req_param->die_id = die->die_id;
>> +	req_param->port_id = port->port_id;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev,
>> +			"get port crc error count failed, ret = %d.\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
>> +					const struct hccs_die_info *die,
>> +					u8 *all_linked)
>> +{
>> +	struct hccs_die_comm_req_param *req_param;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	if (die->port_num == 0) {
>> +		*all_linked = 1;
>> +		return 0;
>> +	}
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>> +	req_param->chip_id = die->chip->chip_id;
>> +	req_param->die_id = die->die_id;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev,
>> +			"get link status of all ports failed on die%u, ret = %d.\n",
>> +			die->die_id, ret);
>> +		return ret;
>> +	}
>> +
>> +	*all_linked = *((u8 *)&desc.rsp.data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
>> +					     const struct hccs_die_info *die,
>> +					     u8 *full_lane)
>> +{
>> +	struct hccs_die_comm_req_param *req_param;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	if (die->port_num == 0) {
>> +		*full_lane = 1;
>> +		return 0;
>> +	}
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>> +	req_param->chip_id = die->chip->chip_id;
>> +	req_param->die_id = die->die_id;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
>> +			die->die_id, ret);
>> +		return ret;
>> +	}
>> +
>> +	*full_lane = *((u8 *)&desc.rsp.data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
>> +					  const struct hccs_die_info *die,
>> +					  u64 *total_crc_err_cnt)
>> +{
>> +	struct hccs_die_comm_req_param *req_param;
>> +	struct hccs_desc desc;
>> +	int ret;
>> +
>> +	if (die->port_num == 0) {
>> +		*total_crc_err_cnt = 0;
>> +		return 0;
>> +	}
>> +
>> +	hccs_init_req_desc(&desc);
>> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>> +	req_param->chip_id = die->chip->chip_id;
>> +	req_param->die_id = die->die_id;
>> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
>> +	if (ret) {
>> +		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
>> +			die->die_id, ret);
>> +		return ret;
>> +	}
>> +
>> +	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
>> +
>> +	return 0;
>> +}
>> +
>> +static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
>> +{
>> +	struct kobj_attribute *kobj_attr;
>> +
>> +	kobj_attr = container_of(attr, struct kobj_attribute, attr);
>> +
>> +	return kobj_attr->show(k, kobj_attr, buf);
>> +}
>> +
>> +static const struct sysfs_ops hccs_comm_ops = {
>> +	.show = hccs_show,
>> +};
>> +
>> +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +
>> +	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
>> +}
>> +
>> +static struct kobj_attribute hccs_type_attr =
>> +			__ATTR(type, 0444, type_show, NULL);
> It is better to use __ATTR_RO.
Ack
>
>> +
>> +static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			      char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +
>> +	return sysfs_emit(buf, "x%u\n", port->lane_mode);
>> +}
>> +
>> +static struct kobj_attribute lane_mode_attr =
>> +			__ATTR(lane_mode, 0444, lane_mode_show, NULL);
>> +
>> +static ssize_t enable_show(struct kobject *kobj,
>> +				 struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +
>> +	return sysfs_emit(buf, "%u\n", port->enable);
>> +}
>> +
>> +static struct kobj_attribute port_enable_attr =
>> +			__ATTR(enable, 0444, enable_show, NULL);
> Ditto.
Ok, all port attribute definition will be modified.
But the definition of //attributes in chip and die directory cannot use 
__ATTR_RO,
because the //attribute file //name in two directories are the same and 
have to need different show() function.
>
>> +
>> +static ssize_t cur_lane_num_show(struct kobject *kobj,
>> +				 struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +	struct hccs_dev *hdev = port->die->chip->hdev;
>> +	struct hccs_link_status link_status = {0};
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_query_port_link_status(hdev, port, &link_status);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return sysfs_emit(buf, "%u\n", link_status.lane_num);
>> +}
>> +
>> +static struct kobj_attribute cur_lane_num_attr =
>> +			__ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
>> +
>> +static ssize_t link_fsm_show(struct kobject *kobj,
>> +			     struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +	struct hccs_dev *hdev = port->die->chip->hdev;
>> +	struct hccs_link_status link_status = {0};
>> +	const struct {
>> +		u8 link_fsm;
>> +		char *str;
>> +	} link_fsm_map[] = {
>> +		{HCCS_PORT_RESET, "reset"},
>> +		{HCCS_PORT_SETUP, "setup"},
>> +		{HCCS_PORT_CONFIG, "config"},
>> +		{HCCS_PORT_READY, "link-up"},
>> +	};
>> +	const char *link_fsm_str = "unknown";
>> +	size_t i;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_query_port_link_status(hdev, port, &link_status);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
>> +		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
>> +			link_fsm_str = link_fsm_map[i].str;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return sysfs_emit(buf, "%s\n", link_fsm_str);
>> +}
>> +
>> +static struct kobj_attribute link_fsm_attr =
>> +			__ATTR(link_fsm, 0444, link_fsm_show, NULL);
>> +
>> +static ssize_t lane_mask_show(struct kobject *kobj,
>> +			      struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +	struct hccs_dev *hdev = port->die->chip->hdev;
>> +	struct hccs_link_status link_status = {0};
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_query_port_link_status(hdev, port, &link_status);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
>> +}
>> +
>> +static struct kobj_attribute lane_mask_attr =
>> +			__ATTR(lane_mask, 0444, lane_mask_show, NULL);
>> +
>> +static ssize_t crc_err_cnt_show(struct kobject *kobj,
>> +				struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_port_info *port = kobj_to_port_info(kobj);
>> +	struct hccs_dev *hdev = port->die->chip->hdev;
>> +	u64 crc_err_cnt;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
>> +}
>> +
>> +static struct kobj_attribute crc_err_cnt_attr =
>> +			__ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
>> +
>> +static struct attribute *hccs_port_default_attrs[] = {
>> +	&hccs_type_attr.attr,
>> +	&lane_mode_attr.attr,
>> +	&port_enable_attr.attr,
>> +	&cur_lane_num_attr.attr,
>> +	&link_fsm_attr.attr,
>> +	&lane_mask_attr.attr,
>> +	&crc_err_cnt_attr.attr,
>> +	NULL,
>> +};
>> +ATTRIBUTE_GROUPS(hccs_port_default);
>> +
>> +static const struct kobj_type hccs_port_type = {
>> +	.sysfs_ops = &hccs_comm_ops,
>> +	.default_groups = hccs_port_default_groups,
>> +};
>> +
>> +static ssize_t all_linked_on_die_show(struct kobject *kobj,
>> +				      struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_die_info *die = kobj_to_die_info(kobj);
>> +	struct hccs_dev *hdev = die->chip->hdev;
>> +	u8 all_linked;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return sysfs_emit(buf, "%u\n", all_linked);
>> +}
>> +static struct kobj_attribute all_linked_on_die_attr =
>> +		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
>> +
>> +static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
>> +					    struct kobj_attribute *attr,
>> +					    char *buf)
>> +{
>> +	const struct hccs_die_info *die = kobj_to_die_info(kobj);
>> +	struct hccs_dev *hdev = die->chip->hdev;
>> +	u8 full_lane;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return sysfs_emit(buf, "%u\n", full_lane);
>> +}
>> +static struct kobj_attribute linked_full_lane_on_die_attr =
>> +	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
>> +
>> +static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
>> +					   struct kobj_attribute *attr,
>> +					   char *buf)
>> +{
>> +	const struct hccs_die_info *die = kobj_to_die_info(kobj);
>> +	struct hccs_dev *hdev = die->chip->hdev;
>> +	u64 total_crc_err_cnt;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
>> +	mutex_unlock(&hdev->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
>> +}
>> +static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
>> +	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
>> +
>> +static struct attribute *hccs_die_default_attrs[] = {
>> +	&all_linked_on_die_attr.attr,
>> +	&linked_full_lane_on_die_attr.attr,
>> +	&crc_err_cnt_sum_on_die_attr.attr,
>> +	NULL,
>> +};
>> +ATTRIBUTE_GROUPS(hccs_die_default);
>> +
>> +static const struct kobj_type hccs_die_type = {
>> +	.sysfs_ops = &hccs_comm_ops,
>> +	.default_groups = hccs_die_default_groups,
>> +};
>> +
>> +static ssize_t all_linked_on_chip_show(struct kobject *kobj,
>> +				       struct kobj_attribute *attr, char *buf)
>> +{
>> +	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
>> +	struct hccs_dev *hdev = chip->hdev;
>> +	const struct hccs_die_info *die;
>> +	u8 all_linked = 1;
>> +	u8 i, tmp;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	for (i = 0; i < chip->die_num; i++) {
>> +		die = &chip->dies[i];
>> +		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
>> +		if (ret) {
>> +			mutex_unlock(&hdev->lock);
>> +			return ret;
>> +		}
>> +		if (tmp != all_linked) {
>> +			all_linked = 0;
>> +			break;
>> +		}
>> +	}
>> +	mutex_unlock(&hdev->lock);
>> +
>> +	return sysfs_emit(buf, "%u\n", all_linked);
>> +}
>> +static struct kobj_attribute all_linked_on_chip_attr =
>> +		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
>> +
>> +static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
>> +					     struct kobj_attribute *attr,
>> +					     char *buf)
>> +{
>> +	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
>> +	struct hccs_dev *hdev = chip->hdev;
>> +	const struct hccs_die_info *die;
>> +	u8 full_lane = 1;
>> +	u8 i, tmp;
>> +	int ret;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	for (i = 0; i < chip->die_num; i++) {
>> +		die = &chip->dies[i];
>> +		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
>> +		if (ret) {
>> +			mutex_unlock(&hdev->lock);
>> +			return ret;
>> +		}
>> +		if (tmp != full_lane) {
>> +			full_lane = 0;
>> +			break;
>> +		}
>> +	}
>> +	mutex_unlock(&hdev->lock);
>> +
>> +	return sysfs_emit(buf, "%u\n", full_lane);
>> +}
>> +static struct kobj_attribute linked_full_lane_on_chip_attr =
>> +	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
>> +
>> +static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
>> +					    struct kobj_attribute *attr,
>> +					    char *buf)
>> +{
>> +	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
>> +	u64 crc_err_cnt, total_crc_err_cnt = 0;
>> +	struct hccs_dev *hdev = chip->hdev;
>> +	const struct hccs_die_info *die;
>> +	int ret;
>> +	u16 i;
>> +
>> +	mutex_lock(&hdev->lock);
>> +	for (i = 0; i < chip->die_num; i++) {
>> +		die = &chip->dies[i];
>> +		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
>> +		if (ret) {
>> +			mutex_unlock(&hdev->lock);
>> +			return ret;
>> +		}
>> +
>> +		total_crc_err_cnt += crc_err_cnt;
>> +	}
>> +	mutex_unlock(&hdev->lock);
>> +
>> +	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
>> +}
>> +static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
>> +		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
>> +
>> +static struct attribute *hccs_chip_default_attrs[] = {
>> +	&all_linked_on_chip_attr.attr,
>> +	&linked_full_lane_on_chip_attr.attr,
>> +	&crc_err_cnt_sum_on_chip_attr.attr,
>> +	NULL,
>> +};
>> +ATTRIBUTE_GROUPS(hccs_chip_default);
>> +
>> +static const struct kobj_type hccs_chip_type = {
>> +	.sysfs_ops = &hccs_comm_ops,
>> +	.default_groups = hccs_chip_default_groups,
>> +};
>> +
>> +static void hccs_remove_die_dir(struct hccs_die_info *die)
>> +{
>> +	struct hccs_port_info *port;
>> +	u8 i;
>> +
>> +	for (i = 0; i < die->port_num; i++) {
>> +		port = &die->ports[i];
>> +		if (port->dir_created)
>> +			kobject_put(&port->kobj);
>> +	}
>> +
>> +	kobject_put(&die->kobj);
>> +}
>> +
>> +static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
>> +{
>> +	struct hccs_die_info *die;
>> +	u8 i;
>> +
>> +	for (i = 0; i < chip->die_num; i++) {
>> +		die = &chip->dies[i];
>> +		if (die->dir_created)
>> +			hccs_remove_die_dir(die);
>> +	}
>> +
>> +	kobject_put(&chip->kobj);
>> +}
>> +
>> +static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
>> +{
>> +	u8 i;
>> +
>> +	for (i = 0; i < hdev->chip_num; i++)
>> +		hccs_remove_chip_dir(&hdev->chips[i]);
>> +}
>> +
>> +static int hccs_create_hccs_dir(struct hccs_dev *hdev,
>> +				struct hccs_die_info *die,
>> +				struct hccs_port_info *port)
>> +{
>> +	int ret;
>> +
>> +	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
>> +				   &die->kobj, "hccs%d", port->port_id);
>> +	if (ret) {
>> +		kobject_put(&port->kobj);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int hccs_create_die_dir(struct hccs_dev *hdev,
>> +			       struct hccs_chip_info *chip,
>> +			       struct hccs_die_info *die)
>> +{
>> +	struct hccs_port_info *port;
>> +	int ret;
>> +	u16 i;
>> +
>> +	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
>> +				   &chip->kobj, "die%d", die->die_id);
>> +	if (ret) {
>> +		kobject_put(&die->kobj);
>> +		return ret;
>> +	}
>> +
>> +	for (i = 0; i < die->port_num; i++) {
>> +		port = &die->ports[i];
>> +		ret = hccs_create_hccs_dir(hdev, die, port);
>> +		if (ret) {
>> +			dev_err(hdev->dev, "create hccs%d dir failed.\n",
>> +				port->port_id);
>> +			goto err;
>> +		}
>> +		port->dir_created = true;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	hccs_remove_die_dir(die);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_create_chip_dir(struct hccs_dev *hdev,
>> +				struct hccs_chip_info *chip)
>> +{
>> +	struct hccs_die_info *die;
>> +	int ret;
>> +	u16 id;
>> +
>> +	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
>> +				   &hdev->dev->kobj, "chip%d", chip->chip_id);
>> +	if (ret) {
>> +		kobject_put(&chip->kobj);
>> +		return ret;
>> +	}
>> +
>> +	for (id = 0; id < chip->die_num; id++) {
>> +		die = &chip->dies[id];
>> +		ret = hccs_create_die_dir(hdev, chip, die);
>> +		if (ret)
>> +			goto err;
>> +		die->dir_created = true;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	hccs_remove_chip_dir(chip);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_create_topo_dirs(struct hccs_dev *hdev)
>> +{
>> +	struct hccs_chip_info *chip;
>> +	u8 id, k;
>> +	int ret;
>> +
>> +	for (id = 0; id < hdev->chip_num; id++) {
>> +		chip = &hdev->chips[id];
>> +		ret = hccs_create_chip_dir(hdev, chip);
>> +		if (ret) {
>> +			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	for (k = 0; k < id; k++)
>> +		hccs_remove_chip_dir(&hdev->chips[k]);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hccs_probe(struct platform_device *pdev)
>> +{
>> +	struct acpi_device *acpi_dev;
>> +	struct hccs_dev *hdev;
>> +	int rc;
>> +
>> +	if (acpi_disabled) {
>> +		dev_err(&pdev->dev, "acpi is disabled.\n");
>> +		return -ENODEV;
>> +	}
>> +	acpi_dev = ACPI_COMPANION(&pdev->dev);
>> +	if (!acpi_dev)
>> +		return -ENODEV;
>> +
>> +	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
>> +	if (!hdev)
>> +		return -ENOMEM;
>> +	hdev->acpi_dev = acpi_dev;
>> +	hdev->dev = &pdev->dev;
>> +	platform_set_drvdata(pdev, hdev);
>> +
>> +	mutex_init(&hdev->lock);
>> +	rc = hccs_get_pcc_chan_id(hdev);
>> +	if (rc)
>> +		return rc;
>> +	rc = hccs_register_pcc_channel(hdev);
>> +	if (rc)
>> +		return rc;
>> +
>> +	rc = hccs_get_dev_caps(hdev);
>> +	if (rc)
>> +		goto unregister_pcc_chan;
>> +
>> +	rc = hccs_get_hw_info(hdev);
>> +	if (rc)
>> +		goto unregister_pcc_chan;
>> +
>> +	rc = hccs_create_topo_dirs(hdev);
>> +	if (rc)
>> +		goto unregister_pcc_chan;
>> +
>> +	return 0;
>> +
>> +unregister_pcc_chan:
>> +	hccs_unregister_pcc_channel(hdev);
>> +
>> +	return rc;
>> +}
>> +
>> +static int hccs_remove(struct platform_device *pdev)
>> +{
>> +	struct hccs_dev *hdev = platform_get_drvdata(pdev);
>> +
>> +	hccs_remove_topo_dirs(hdev);
>> +	hccs_unregister_pcc_channel(hdev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct acpi_device_id hccs_acpi_match[] = {
>> +	{ "HISI04B1", },
>> +};
> It is better to add MODULE_DEVICE_TABLE to autoload the driver.
I tested that this macro doesn't help to autoload driver if select this 
driver as 'M'.
How does it work?

Best regards,
Huisong
>
[snip]

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-07-25  8:59     ` Wei Xu
@ 2023-07-26  9:56       ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-07-26  9:56 UTC (permalink / raw)
  To: Wei Xu, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong


在 2023/7/25 16:59, Wei Xu 写道:
> Hi Huisong,
>
> On 2023/7/25 15:57, Huisong Li wrote:
>> Document the sysfs attributes description provided by HCCS driver on
>> Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>> ---
>>   .../sysfs-devices-platform-kunpeng_hccs       | 76 +++++++++++++++++++
>>   MAINTAINERS                                   |  1 +
>>   2 files changed, 77 insertions(+)
>>   create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>>
>> diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>> new file mode 100644
>> index 000000000000..83ebed801249
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>> @@ -0,0 +1,76 @@
>> +What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
>> +		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
>> +		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
>> +Date:		May 2023
> Please update the date to "November 2023" to match the 6.6 kernel.
Ack. will fix in next version.
>
>> +KernelVersion:	6.6
>> +Contact:	Huisong Li <lihuisong@huawei.org>
>> +Description:
>> +		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
>> +		contains read-only attributes exposing some summarization
>> +		information of all HCCS ports under a specified chip.
>> +		The X in 'chipX' indicates the Xth chip on platform.
>> +
>> +		There are following attributes in this directory:
>> +		================= ==== =========================================
>> +		all_linked:       (RO) if all enabled ports on this chip are
>> +				       linked (bool).
>> +		linked_full_lane: (RO) if all linked ports on this chip are full
>> +				       lane (bool).
>> +		crc_err_cnt:      (RO) total CRC err count for all ports on this
>> +				       chip.
>> +		============= ==== =============================================
>> +
>> +What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
>> +Date:		May 2023
> Ditto.
Ack
>
>> +KernelVersion:	6.6
>> +Contact:	Huisong Li <lihuisong@huawei.org>
>> +Description:
>> +		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
>> +		contains read-only attributes exposing some summarization
>> +		information of all HCCS ports under a specified die.
>> +		The Y in 'dieY' indicates the hardware id of the die on chip who
>> +		has chip id X.
>> +
>> +		There are following attributes in this directory:
>> +		================= ==== =========================================
>> +		all_linked:       (RO) if all enabled ports on this die are
>> +				       linked (bool).
>> +		linked_full_lane: (RO) if all linked ports on this die are full
>> +				       lane (bool).
>> +		crc_err_cnt:      (RO) total CRC err count for all ports on this
>> +				       die.
>> +		============= ==== =============================================
>> +
>> +What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
>> +		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
>> +Date:		May 2023
> Ditto.

Ack

>
>> +KernelVersion:	6.6
>> +Contact:	Huisong Li <lihuisong@huawei.org>
>> +Description:
>> +		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
>> +		contains read-only attributes exposing information about
>> +		a HCCS port. The N value in 'hccsN' indicates this port id.
>> +		The X in 'chipX' indicates the ID of the chip to which the
>> +		HCCS port belongs. For example, X ranges from to 'n - 1' if the
>> +		chip number on platform is n.
>> +		The Y in 'dieY' indicates the hardware id of the die to which
>> +		the hccs port belongs.
>> +
>> +		The HCCS port have the following attributes:
>> +		============= ==== =============================================
>> +		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
>> +		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
>> +		enable:       (RO) indicate if this port is enabled (bool).
>> +		cur_lane_num: (RO) current lane number of this port.
>> +		lane_mask:    (RO) current lane mask of this port, every bit
>> +			           indicates a lane.
>> +		crc_err_cnt:  (RO) CRC err count on this port.
>> +		============= ==== =============================================
>> +		Note: type, lane_mode and enable are fixed attributes on
>> +		      running platform.
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 4e55ff992171..7a34bab232eb 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9474,6 +9474,7 @@ F:	drivers/crypto/hisilicon/zip/
>>   HISILICON KUNPENG SOC HCCS DRIVER
>>   M:	Huisong Li <lihuisong@huawei.com>
>>   S:	Maintained
>> +F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>>   F:	drivers/soc/hisilicon/kunpeng_hccs.c
>>   F:	drivers/soc/hisilicon/kunpeng_hccs.h
>>   
>>
> .

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH RESEND v3 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-26  9:54       ` lihuisong (C)
@ 2023-07-27  3:51         ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-07-27  3:51 UTC (permalink / raw)
  To: Wei Xu, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong


在 2023/7/26 17:54, lihuisong (C) 写道:
> Hi Wei,
>
> Thanks for your review.
>
>
> 在 2023/7/25 16:55, Wei Xu 写道:
>> Hi Huisong,
>>
>> On 2023/7/25 15:57, Huisong Li wrote:
>>> The Huawei Cache-Coherent System (HCCS) is a bus protocol standard
>>> for ensuring cache coherent on HiSilicon SoC. The performance of
>>> the application may be affected if some hccs ports are in non-full
>>> lane status, have a large number of CRC errors and so on.
>>>
>>> This driver provides the query interface of the health status and
>>> port information of HCCS on Kunpeng SoC.
>>>
>>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>>> ---
>>>   MAINTAINERS                          |    6 +
>>>   drivers/soc/Kconfig                  |    1 +
>>>   drivers/soc/Makefile                 |    1 +
>>>   drivers/soc/hisilicon/Kconfig        |   19 +
>>>   drivers/soc/hisilicon/Makefile       |    2 +
>>>   drivers/soc/hisilicon/kunpeng_hccs.c | 1288 
>>> ++++++++++++++++++++++++++
>>>   drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
>>>   7 files changed, 1513 insertions(+)
>>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>>>   create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index a5c16bb92fe2..4e55ff992171 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -9471,6 +9471,12 @@ S:    Maintained
>>>   F:    Documentation/ABI/testing/debugfs-hisi-zip
>>>   F:    drivers/crypto/hisilicon/zip/
>>>   +HISILICON KUNPENG SOC HCCS DRIVER
>>> +M:    Huisong Li <lihuisong@huawei.com>
>>> +S:    Maintained
>>> +F:    drivers/soc/hisilicon/kunpeng_hccs.c
>>> +F:    drivers/soc/hisilicon/kunpeng_hccs.h
>>> +
>>>   HMM - Heterogeneous Memory Management
>>>   M:    Jérôme Glisse <jglisse@redhat.com>
>>>   L:    linux-mm@kvack.org
>>> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
>>> index 4e176280113a..d21e75d69294 100644
>>> --- a/drivers/soc/Kconfig
>>> +++ b/drivers/soc/Kconfig
>>> @@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
>>>   source "drivers/soc/canaan/Kconfig"
>>>   source "drivers/soc/fsl/Kconfig"
>>>   source "drivers/soc/fujitsu/Kconfig"
>>> +source "drivers/soc/hisilicon/Kconfig"
>>>   source "drivers/soc/imx/Kconfig"
>>>   source "drivers/soc/ixp4xx/Kconfig"
>>>   source "drivers/soc/litex/Kconfig"
>>> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
>>> index 3b0f9fb3b5c8..531f46f3ad98 100644
>>> --- a/drivers/soc/Makefile
>>> +++ b/drivers/soc/Makefile
>>> @@ -14,6 +14,7 @@ obj-$(CONFIG_MACH_DOVE)        += dove/
>>>   obj-y                += fsl/
>>>   obj-y                += fujitsu/
>>>   obj-$(CONFIG_ARCH_GEMINI)    += gemini/
>>> +obj-y                += hisilicon/
>>>   obj-y                += imx/
>>>   obj-y                += ixp4xx/
>>>   obj-$(CONFIG_SOC_XWAY)        += lantiq/
>>> diff --git a/drivers/soc/hisilicon/Kconfig 
>>> b/drivers/soc/hisilicon/Kconfig
>>> new file mode 100644
>>> index 000000000000..87a1f15cbedb
>>> --- /dev/null
>>> +++ b/drivers/soc/hisilicon/Kconfig
>>> @@ -0,0 +1,19 @@
>>> +# SPDX-License-Identifier: GPL-2.0-only
>>> +
>>> +menu "Hisilicon SoC drivers"
>>> +    depends on ARCH_HISI || COMPILE_TEST
>>> +
>>> +config KUNPENG_HCCS
>>> +    tristate "HCCS driver on Kunpeng SoC"
>>> +    depends on ACPI
>>> +    depends on ARM64 || COMPILE_TEST
>>> +    help
>>> +      The Huawei Cache-Coherent System (HCCS) is a bus protocol 
>>> standard
>>> +      for ensuring cache coherent on HiSilicon SoC. The performance of
>>> +      the application may be affected if some hccs ports are in 
>>> non-full
>>> +      lane status, have a large number of CRC errors and so on.
>>> +
>>> +      Say M here if you want to include support for querying the 
>>> health
>>> +      status and port information of HCCS on Kunpeng SoC.
>>> +
>>> +endmenu
>>> diff --git a/drivers/soc/hisilicon/Makefile 
>>> b/drivers/soc/hisilicon/Makefile
>>> new file mode 100644
>>> index 000000000000..226e747e70d6
>>> --- /dev/null
>>> +++ b/drivers/soc/hisilicon/Makefile
>>> @@ -0,0 +1,2 @@
>>> +# SPDX-License-Identifier: GPL-2.0-only
>>> +obj-$(CONFIG_KUNPENG_HCCS)    += kunpeng_hccs.o
>>> diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c 
>>> b/drivers/soc/hisilicon/kunpeng_hccs.c
>>> new file mode 100644
>>> index 000000000000..d7f4bb04deb3
>>> --- /dev/null
>>> +++ b/drivers/soc/hisilicon/kunpeng_hccs.c
>>> @@ -0,0 +1,1288 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + * The Huawei Cache-Coherent System (HCCS) is a bus protocol 
>>> standard for
>>> + * ensuring cache coherent on HiSilicon SoC.
>>> + *
>>> + * Copyright (c) 2023 Hisilicon Limited.
>>> + * Author: Huisong Li <lihuisong@huawei.com>
>>> + *
>>> + * HCCS driver for Kunpeng SoC provides the following features:
>>> + * - Retrieve the following information about each port:
>>> + *    - port type
>>> + *    - lane mode
>>> + *    - enable
>>> + *    - current lane mode
>>> + *    - link state machine
>>> + *    - lane mask
>>> + *    - CRC error count
>>> + *
>>> + * - Retrieve the following information about all the ports on the 
>>> chip or
>>> + *   the die:
>>> + *    - if all enabled ports are in linked
>>> + *    - if all linked ports are in full lane
>>> + *    - CRC error count sum
>>> + */
>>> +#include <linux/sysfs.h>
>>> +#include <linux/acpi.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kobject.h>
>> Please remove the useless including of the io.h and kobject.h.
> useless header, indeed. will remove in v4.
>>
>>> +#include <linux/iopoll.h>
>>> +#include <linux/platform_device.h>
>> It is better to put all the linux headers into alphabetical order.
> Ack
>>
>>> +#include <acpi/pcc.h>
>>> +
>>> +#include "kunpeng_hccs.h"
>>> +
>>> +/* PCC defines */
>>> +#define HCCS_PCC_SIGNATURE_MASK        0x50434300
>>> +#define HCCS_PCC_STATUS_CMD_COMPLETE    BIT(0)
>>> +
>>> +/*
>>> + * Arbitrary retries in case the remote processor is slow to respond
>>> + * to PCC commands
>>> + */
>>> +#define HCCS_PCC_CMD_WAIT_RETRIES_NUM        500ULL
>>> +#define HCCS_POLL_STATUS_TIME_INTERVAL_US    3
>>> +
>>> +static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
>>> +{
>>> +    return container_of(k, struct hccs_port_info, kobj);
>>> +}
>>> +
>>> +static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
>>> +{
>>> +    return container_of(k, struct hccs_die_info, kobj);
>>> +}
>>> +
>>> +static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
>>> +{
>>> +    return container_of(k, struct hccs_chip_info, kobj);
>>> +}
>>> +
>>> +struct hccs_register_ctx {
>>> +    struct device *dev;
>>> +    u8 chan_id;
>>> +    int err;
>>> +};
>>> +
>>> +static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
>>> +                    void *context)
>>> +{
>>> +    struct acpi_resource_generic_register *reg;
>>> +    struct hccs_register_ctx *ctx = context;
>>> +
>>> +    if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
>>> +        return AE_OK;
>>> +
>>> +    reg = &ares->data.generic_reg;
>>> +    if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
>>> +        dev_err(ctx->dev, "Bad register resource.\n");
>>> +        ctx->err = -EINVAL;
>>> +        return AE_ERROR;
>>> +    }
>>> +    ctx->chan_id = reg->access_size;
>>> +
>>> +    return AE_OK;
>>> +}
>>> +
>>> +static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
>>> +{
>>> +    acpi_handle handle = ACPI_HANDLE(hdev->dev);
>>> +    struct hccs_register_ctx ctx = {0};
>>> +    acpi_status status;
>>> +
>>> +    if (!acpi_has_method(handle, METHOD_NAME__CRS))
>>> +        return -ENODEV;
>>> +
>>> +    ctx.dev = hdev->dev;
>>> +    status = acpi_walk_resources(handle, METHOD_NAME__CRS,
>>> +                     hccs_get_register_cb, &ctx);
>>> +    if (ACPI_FAILURE(status))
>>> +        return ctx.err;
>>> +    hdev->chan_id = ctx.chan_id;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, 
>>> int ret)
>>> +{
>>> +    if (ret < 0)
>>> +        pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
>>> +             *(u8 *)msg, ret);
>>> +    else
>>> +        pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
>>> +             *(u8 *)msg, ret);
>>> +}
>>> +
>>> +static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>>> +
>>> +    if (cl_info->pcc_comm_addr)
>>> +        iounmap(cl_info->pcc_comm_addr);
>>> +    pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
>>> +}
>>> +
>>> +static int hccs_register_pcc_channel(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>>> +    struct mbox_client *cl = &cl_info->client;
>>> +    struct pcc_mbox_chan *pcc_chan;
>>> +    struct device *dev = hdev->dev;
>>> +    int rc;
>>> +
>>> +    cl->dev = dev;
>>> +    cl->tx_block = false;
>>> +    cl->knows_txdone = true;
>>> +    cl->tx_done = hccs_chan_tx_done;
>>> +    pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
>>> +    if (IS_ERR(pcc_chan)) {
>>> +        dev_err(dev, "PPC channel request failed.\n");
>>> +        rc = -ENODEV;
>>> +        goto out;
>>> +    }
>>> +    cl_info->pcc_chan = pcc_chan;
>>> +    cl_info->mbox_chan = pcc_chan->mchan;
>>> +
>>> +    /*
>>> +     * pcc_chan->latency is just a nominal value. In reality the 
>>> remote
>>> +     * processor could be much slower to reply. So add an arbitrary 
>>> amount
>>> +     * of wait on top of nominal.
>>> +     */
>>> +    cl_info->deadline_us =
>>> +            HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
>>> +    if (cl_info->mbox_chan->mbox->txdone_irq) {
>>> +        dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
>>> +        rc = -EINVAL;
>>> +        goto err_mbx_channel_free;
>>> +    }
>>> +
>>> +    if (pcc_chan->shmem_base_addr) {
>>> +        cl_info->pcc_comm_addr = (void __force *)ioremap(
>>> +            pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
>>> +        if (!cl_info->pcc_comm_addr) {
>>> +            dev_err(dev, "Failed to ioremap PCC communication 
>>> region for channel-%d.\n",
>>> +                hdev->chan_id);
>>> +            rc = -ENOMEM;
>>> +            goto err_mbx_channel_free;
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +err_mbx_channel_free:
>>> +    pcc_mbox_free_channel(cl_info->pcc_chan);
>>> +out:
>>> +    return rc;
>>> +}
>>> +
>>> +static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>>> +    struct acpi_pcct_shared_memory *comm_base = 
>>> cl_info->pcc_comm_addr;
>>> +    u16 status;
>>> +    int ret;
>>> +
>>> +    /*
>>> +     * Poll PCC status register every 3us(delay_us) for maximum of
>>> +     * deadline_us(timeout_us) until PCC command complete bit is 
>>> set(cond)
>>> +     */
>>> +    ret = readw_poll_timeout(&comm_base->status, status,
>>> +                 status & HCCS_PCC_STATUS_CMD_COMPLETE,
>>> +                 HCCS_POLL_STATUS_TIME_INTERVAL_US,
>>> +                 cl_info->deadline_us);
>>> +    if (unlikely(ret))
>>> +        dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", 
>>> ret);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
>>> +                 struct hccs_desc *desc)
>>> +{
>>> +    struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
>>> +    struct acpi_pcct_shared_memory *comm_base = 
>>> cl_info->pcc_comm_addr;
>>> +    void *comm_space = (void *)(comm_base + 1);
>>> +    struct hccs_fw_inner_head *fw_inner_head;
>>> +    struct acpi_pcct_shared_memory tmp = {0};
>>> +    u16 comm_space_size;
>>> +    int ret;
>>> +
>>> +    /* Write signature for this subspace */
>>> +    tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
>>> +    /* Write to the shared command region */
>>> +    tmp.command = cmd;
>>> +    /* Clear cmd complete bit */
>>> +    tmp.status = 0;
>>> +    memcpy_toio(comm_base, (void *)&tmp,
>>> +            sizeof(struct acpi_pcct_shared_memory));
>>> +
>>> +    /* Copy the message to the PCC comm space */
>>> +    comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
>>> +                sizeof(struct acpi_pcct_shared_memory);
>>> +    memcpy_toio(comm_space, (void *)desc, comm_space_size);
>>> +
>>> +    /* Ring doorbell */
>>> +    ret = mbox_send_message(cl_info->mbox_chan, &cmd);
>>> +    if (ret < 0) {
>>> +        dev_err(hdev->dev, "Send PCC mbox message failed, ret = 
>>> %d.\n",
>>> +            ret);
>>> +        goto end;
>>> +    }
>>> +
>>> +    /* Wait for completion */
>>> +    ret = hccs_check_chan_cmd_complete(hdev);
>>> +    if (ret)
>>> +        goto end;
>>> +
>>> +    /* Copy response data */
>>> +    memcpy_fromio((void *)desc, comm_space, comm_space_size);
>>> +    fw_inner_head = &desc->rsp.fw_inner_head;
>>> +    if (fw_inner_head->retStatus) {
>>> +        dev_err(hdev->dev, "Execute PCC command failed, error code 
>>> = %u.\n",
>>> +            fw_inner_head->retStatus);
>>> +        ret = -EIO;
>>> +    }
>>> +
>>> +end:
>>> +    mbox_client_txdone(cl_info->mbox_chan, ret);
>>> +    return ret;
>>> +}
>>> +
>>> +static void hccs_init_req_desc(struct hccs_desc *desc)
>>> +{
>>> +    struct hccs_req_desc *req = &desc->req;
>>> +
>>> +    memset(desc, 0, sizeof(*desc));
>>> +    req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
>>> +}
>>> +
>>> +static int hccs_get_dev_caps(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "Get device capabilities failed, ret = 
>>> %d.\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +    memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "query system chip number failed, ret = 
>>> %d.\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    hdev->chip_num = *((u8 *)&desc.rsp.data);
>>> +    if (!hdev->chip_num) {
>>> +        dev_err(hdev->dev, "chip num obtained from firmware is 
>>> zero.\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_chip_info(struct hccs_dev *hdev,
>>> +                  struct hccs_chip_info *chip)
>>> +{
>>> +    struct hccs_die_num_req_param *req_param;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_die_num_req_param *)desc.req.data;
>>> +    req_param->chip_id = chip->chip_id;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    chip->die_num = *((u8 *)&desc.rsp.data);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_chip_info *chip;
>>> +    int ret;
>>> +    u8 idx;
>>> +
>>> +    ret = hccs_query_chip_num_on_platform(hdev);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "query chip number on platform failed, 
>>> ret = %d.\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    hdev->chips = devm_kzalloc(hdev->dev,
>>> +                hdev->chip_num * sizeof(struct hccs_chip_info),
>>> +                GFP_KERNEL);
>>> +    if (!hdev->chips) {
>>> +        dev_err(hdev->dev, "allocate all chips memory failed.\n");
>>> +        return -ENOMEM;
>>> +    }
>>> +
>>> +    for (idx = 0; idx < hdev->chip_num; idx++) {
>>> +        chip = &hdev->chips[idx];
>>> +        chip->chip_id = idx;
>>> +        ret = hccs_get_chip_info(hdev, chip);
>>> +        if (ret) {
>>> +            dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
>>> +                idx, ret);
>>> +            return ret;
>>> +        }
>>> +        chip->hdev = hdev;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 
>>> chip_id,
>>> +                       u8 die_idx, struct hccs_die_info *die)
>>> +{
>>> +    struct hccs_die_info_req_param *req_param;
>>> +    struct hccs_die_info_rsp_data *rsp_data;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_die_info_req_param *)desc.req.data;
>>> +    req_param->chip_id = chip_id;
>>> +    req_param->die_idx = die_idx;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
>>> +    die->die_id = rsp_data->die_id;
>>> +    die->port_num = rsp_data->port_num;
>>> +    die->min_port_id = rsp_data->min_port_id;
>>> +    die->max_port_id = rsp_data->max_port_id;
>>> +    if (die->min_port_id > die->max_port_id) {
>>> +        dev_err(hdev->dev, "min port id(%u) > max port id(%u) on 
>>> die_idx(%u).\n",
>>> +            die->min_port_id, die->max_port_id, die_idx);
>>> +        return -EINVAL;
>>> +    }
>>> +    if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
>>> +        dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too 
>>> big.\n",
>>> +            die->max_port_id, die_idx);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
>>> +{
>>> +    struct device *dev = hdev->dev;
>>> +    struct hccs_chip_info *chip;
>>> +    struct hccs_die_info *die;
>>> +    u8 i, j;
>>> +    int ret;
>>> +
>>> +    for (i = 0; i < hdev->chip_num; i++) {
>>> +        chip = &hdev->chips[i];
>>> +        if (!chip->die_num)
>>> +            continue;
>>> +
>>> +        chip->dies = devm_kzalloc(hdev->dev,
>>> +                chip->die_num * sizeof(struct hccs_die_info),
>>> +                GFP_KERNEL);
>>> +        if (!chip->dies) {
>>> +            dev_err(dev, "allocate all dies memory on chip%u 
>>> failed.\n",
>>> +                i);
>>> +            return -ENOMEM;
>>> +        }
>>> +
>>> +        for (j = 0; j < chip->die_num; j++) {
>>> +            die = &chip->dies[j];
>>> +            ret = hccs_query_die_info_on_chip(hdev, i, j, die);
>>> +            if (ret) {
>>> +                dev_err(dev, "get die idx (%u) info on chip%u 
>>> failed, ret = %d.\n",
>>> +                    j, i, ret);
>>> +                return ret;
>>> +            }
>>> +            die->chip = chip;
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
>>> +                struct hccs_desc *desc,
>>> +                void *buf, size_t buf_len,
>>> +                struct hccs_rsp_head *rsp_head)
>>> +{
>>> +    struct hccs_rsp_head *head;
>>> +    struct hccs_rsp_desc *rsp;
>>> +    int ret;
>>> +
>>> +    ret = hccs_pcc_cmd_send(hdev, opcode, desc);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    rsp = &desc->rsp;
>>> +    head = &rsp->rsp_head;
>>> +    if (head->data_len > buf_len) {
>>> +        dev_err(hdev->dev,
>>> +            "buffer overflow (buf_len = %lu, data_len = %u)!\n",
>>> +            buf_len, head->data_len);
>>> +        return -ENOMEM;
>>> +    }
>>> +
>>> +    memcpy(buf, rsp->data, head->data_len);
>>> +    *rsp_head = *head;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_all_port_attr(struct hccs_dev *hdev,
>>> +                  struct hccs_die_info *die,
>>> +                  struct hccs_port_attr *attrs, u16 size)
>>> +{
>>> +    struct hccs_die_comm_req_param *req_param;
>>> +    struct hccs_req_head *req_head;
>>> +    struct hccs_rsp_head rsp_head;
>>> +    struct hccs_desc desc;
>>> +    size_t left_buf_len;
>>> +    u32 data_len = 0;
>>> +    u8 start_id;
>>> +    u8 *buf;
>>> +    int ret;
>>> +
>>> +    buf = (u8 *)attrs;
>>> +    left_buf_len = sizeof(struct hccs_port_attr) * size;
>>> +    start_id = die->min_port_id;
>>> +    while (start_id <= die->max_port_id) {
>>> +        hccs_init_req_desc(&desc);
>>> +        req_head = &desc.req.req_head;
>>> +        req_head->start_id = start_id;
>>> +        req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>>> +        req_param->chip_id = die->chip->chip_id;
>>> +        req_param->die_id = die->die_id;
>>> +
>>> +        ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
>>> +                       buf + data_len, left_buf_len, &rsp_head);
>>> +        if (ret) {
>>> +            dev_err(hdev->dev,
>>> +                "get the information of port%u on die%u failed, ret 
>>> = %d.\n",
>>> +                start_id, die->die_id, ret);
>>> +            return ret;
>>> +        }
>>> +
>>> +        data_len += rsp_head.data_len;
>>> +        left_buf_len -= rsp_head.data_len;
>>> +        if (unlikely(rsp_head.next_id <= start_id)) {
>>> +            dev_err(hdev->dev,
>>> +                "next port id (%u) is not greater than last start 
>>> id (%u) on die%u.\n",
>>> +                rsp_head.next_id, start_id, die->die_id);
>>> +            return -EINVAL;
>>> +        }
>>> +        start_id = rsp_head.next_id;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
>>> +                     struct hccs_die_info *die)
>>> +{
>>> +    struct hccs_port_attr *attrs;
>>> +    struct hccs_port_info *port;
>>> +    int ret;
>>> +    u8 i;
>>> +
>>> +    attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
>>> +            GFP_KERNEL);
>>> +    if (!attrs)
>>> +        return -ENOMEM;
>>> +
>>> +    ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
>>> +    if (ret)
>>> +        goto out;
>>> +
>>> +    for (i = 0; i < die->port_num; i++) {
>>> +        port = &die->ports[i];
>>> +        port->port_id = attrs[i].port_id;
>>> +        port->port_type = attrs[i].port_type;
>>> +        port->lane_mode = attrs[i].lane_mode;
>>> +        port->enable = attrs[i].enable;
>>> +        port->die = die;
>>> +    }
>>> +
>>> +out:
>>> +    kfree(attrs);
>>> +    return ret;
>>> +}
>>> +
>>> +static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
>>> +{
>>> +
>>> +    struct device *dev = hdev->dev;
>>> +    struct hccs_chip_info *chip;
>>> +    struct hccs_die_info *die;
>>> +    u8 i, j;
>>> +    int ret;
>>> +
>>> +    for (i = 0; i < hdev->chip_num; i++) {
>>> +        chip = &hdev->chips[i];
>>> +        for (j = 0; j < chip->die_num; j++) {
>>> +            die = &chip->dies[j];
>>> +            if (!die->port_num)
>>> +                continue;
>>> +
>>> +            die->ports = devm_kzalloc(dev,
>>> +                die->port_num * sizeof(struct hccs_port_info),
>>> +                GFP_KERNEL);
>>> +            if (!die->ports) {
>>> +                dev_err(dev, "allocate ports memory on chip%u/die%u 
>>> failed.\n",
>>> +                    i, die->die_id);
>>> +                return -ENOMEM;
>>> +            }
>>> +
>>> +            ret = hccs_get_all_port_info_on_die(hdev, die);
>>> +            if (ret) {
>>> +                dev_err(dev, "get die(%u) info on chip%u failed, 
>>> ret = %d.\n",
>>> +                    die->die_id, i, ret);
>>> +                return ret;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_hw_info(struct hccs_dev *hdev)
>>> +{
>>> +    int ret;
>>> +
>>> +    ret = hccs_query_chip_info_on_platform(hdev);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "query chip info on platform failed, ret 
>>> = %d.\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = hccs_query_all_die_info_on_platform(hdev);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "query all die info on platform failed, 
>>> ret = %d.\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = hccs_query_all_port_info_on_platform(hdev);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "query all port info on platform failed, 
>>> ret = %d.\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_query_port_link_status(struct hccs_dev *hdev,
>>> +                       const struct hccs_port_info *port,
>>> +                       struct hccs_link_status *link_status)
>>> +{
>>> +    const struct hccs_die_info *die = port->die;
>>> +    const struct hccs_chip_info *chip = die->chip;
>>> +    struct hccs_port_comm_req_param *req_param;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_port_comm_req_param *)desc.req.data;
>>> +    req_param->chip_id = chip->chip_id;
>>> +    req_param->die_id = die->die_id;
>>> +    req_param->port_id = port->port_id;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev,
>>> +            "get port link status info failed, ret = %d.\n", ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    *link_status = *((struct hccs_link_status *)desc.rsp.data);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
>>> +                       const struct hccs_port_info *port,
>>> +                       u64 *crc_err_cnt)
>>> +{
>>> +    const struct hccs_die_info *die = port->die;
>>> +    const struct hccs_chip_info *chip = die->chip;
>>> +    struct hccs_port_comm_req_param *req_param;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_port_comm_req_param *)desc.req.data;
>>> +    req_param->chip_id = chip->chip_id;
>>> +    req_param->die_id = die->die_id;
>>> +    req_param->port_id = port->port_id;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev,
>>> +            "get port crc error count failed, ret = %d.\n", ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
>>> +                    const struct hccs_die_info *die,
>>> +                    u8 *all_linked)
>>> +{
>>> +    struct hccs_die_comm_req_param *req_param;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    if (die->port_num == 0) {
>>> +        *all_linked = 1;
>>> +        return 0;
>>> +    }
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>>> +    req_param->chip_id = die->chip->chip_id;
>>> +    req_param->die_id = die->die_id;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev,
>>> +            "get link status of all ports failed on die%u, ret = 
>>> %d.\n",
>>> +            die->die_id, ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    *all_linked = *((u8 *)&desc.rsp.data);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
>>> +                         const struct hccs_die_info *die,
>>> +                         u8 *full_lane)
>>> +{
>>> +    struct hccs_die_comm_req_param *req_param;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    if (die->port_num == 0) {
>>> +        *full_lane = 1;
>>> +        return 0;
>>> +    }
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>>> +    req_param->chip_id = die->chip->chip_id;
>>> +    req_param->die_id = die->die_id;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "get lane status of all ports failed on 
>>> die%u, ret = %d.\n",
>>> +            die->die_id, ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    *full_lane = *((u8 *)&desc.rsp.data);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
>>> +                      const struct hccs_die_info *die,
>>> +                      u64 *total_crc_err_cnt)
>>> +{
>>> +    struct hccs_die_comm_req_param *req_param;
>>> +    struct hccs_desc desc;
>>> +    int ret;
>>> +
>>> +    if (die->port_num == 0) {
>>> +        *total_crc_err_cnt = 0;
>>> +        return 0;
>>> +    }
>>> +
>>> +    hccs_init_req_desc(&desc);
>>> +    req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>>> +    req_param->chip_id = die->chip->chip_id;
>>> +    req_param->die_id = die->die_id;
>>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, 
>>> &desc);
>>> +    if (ret) {
>>> +        dev_err(hdev->dev, "get crc error count sum failed on 
>>> die%u, ret = %d.\n",
>>> +            die->die_id, ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static ssize_t hccs_show(struct kobject *k, struct attribute *attr, 
>>> char *buf)
>>> +{
>>> +    struct kobj_attribute *kobj_attr;
>>> +
>>> +    kobj_attr = container_of(attr, struct kobj_attribute, attr);
>>> +
>>> +    return kobj_attr->show(k, kobj_attr, buf);
>>> +}
>>> +
>>> +static const struct sysfs_ops hccs_comm_ops = {
>>> +    .show = hccs_show,
>>> +};
>>> +
>>> +static ssize_t type_show(struct kobject *kobj, struct 
>>> kobj_attribute *attr,
>>> +             char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +
>>> +    return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
>>> +}
>>> +
>>> +static struct kobj_attribute hccs_type_attr =
>>> +            __ATTR(type, 0444, type_show, NULL);
>> It is better to use __ATTR_RO.
> Ack
>>
>>> +
>>> +static ssize_t lane_mode_show(struct kobject *kobj, struct 
>>> kobj_attribute *attr,
>>> +                  char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +
>>> +    return sysfs_emit(buf, "x%u\n", port->lane_mode);
>>> +}
>>> +
>>> +static struct kobj_attribute lane_mode_attr =
>>> +            __ATTR(lane_mode, 0444, lane_mode_show, NULL);
>>> +
>>> +static ssize_t enable_show(struct kobject *kobj,
>>> +                 struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +
>>> +    return sysfs_emit(buf, "%u\n", port->enable);
>>> +}
>>> +
>>> +static struct kobj_attribute port_enable_attr =
>>> +            __ATTR(enable, 0444, enable_show, NULL);
>> Ditto.
> Ok, all port attribute definition will be modified.
> But the definition of //attributes in chip and die directory cannot 
> use __ATTR_RO,
> because the //attribute file //name in two directories are the same 
> and have to need different show() function.
>>
>>> +
>>> +static ssize_t cur_lane_num_show(struct kobject *kobj,
>>> +                 struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +    struct hccs_dev *hdev = port->die->chip->hdev;
>>> +    struct hccs_link_status link_status = {0};
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_query_port_link_status(hdev, port, &link_status);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return sysfs_emit(buf, "%u\n", link_status.lane_num);
>>> +}
>>> +
>>> +static struct kobj_attribute cur_lane_num_attr =
>>> +            __ATTR(cur_lane_num, 0444, cur_lane_num_show, NULL);
>>> +
>>> +static ssize_t link_fsm_show(struct kobject *kobj,
>>> +                 struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +    struct hccs_dev *hdev = port->die->chip->hdev;
>>> +    struct hccs_link_status link_status = {0};
>>> +    const struct {
>>> +        u8 link_fsm;
>>> +        char *str;
>>> +    } link_fsm_map[] = {
>>> +        {HCCS_PORT_RESET, "reset"},
>>> +        {HCCS_PORT_SETUP, "setup"},
>>> +        {HCCS_PORT_CONFIG, "config"},
>>> +        {HCCS_PORT_READY, "link-up"},
>>> +    };
>>> +    const char *link_fsm_str = "unknown";
>>> +    size_t i;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_query_port_link_status(hdev, port, &link_status);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
>>> +        if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
>>> +            link_fsm_str = link_fsm_map[i].str;
>>> +            break;
>>> +        }
>>> +    }
>>> +
>>> +    return sysfs_emit(buf, "%s\n", link_fsm_str);
>>> +}
>>> +
>>> +static struct kobj_attribute link_fsm_attr =
>>> +            __ATTR(link_fsm, 0444, link_fsm_show, NULL);
>>> +
>>> +static ssize_t lane_mask_show(struct kobject *kobj,
>>> +                  struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +    struct hccs_dev *hdev = port->die->chip->hdev;
>>> +    struct hccs_link_status link_status = {0};
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_query_port_link_status(hdev, port, &link_status);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
>>> +}
>>> +
>>> +static struct kobj_attribute lane_mask_attr =
>>> +            __ATTR(lane_mask, 0444, lane_mask_show, NULL);
>>> +
>>> +static ssize_t crc_err_cnt_show(struct kobject *kobj,
>>> +                struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_port_info *port = kobj_to_port_info(kobj);
>>> +    struct hccs_dev *hdev = port->die->chip->hdev;
>>> +    u64 crc_err_cnt;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return sysfs_emit(buf, "%llu\n", crc_err_cnt);
>>> +}
>>> +
>>> +static struct kobj_attribute crc_err_cnt_attr =
>>> +            __ATTR(crc_err_cnt, 0444, crc_err_cnt_show, NULL);
>>> +
>>> +static struct attribute *hccs_port_default_attrs[] = {
>>> +    &hccs_type_attr.attr,
>>> +    &lane_mode_attr.attr,
>>> +    &port_enable_attr.attr,
>>> +    &cur_lane_num_attr.attr,
>>> +    &link_fsm_attr.attr,
>>> +    &lane_mask_attr.attr,
>>> +    &crc_err_cnt_attr.attr,
>>> +    NULL,
>>> +};
>>> +ATTRIBUTE_GROUPS(hccs_port_default);
>>> +
>>> +static const struct kobj_type hccs_port_type = {
>>> +    .sysfs_ops = &hccs_comm_ops,
>>> +    .default_groups = hccs_port_default_groups,
>>> +};
>>> +
>>> +static ssize_t all_linked_on_die_show(struct kobject *kobj,
>>> +                      struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_die_info *die = kobj_to_die_info(kobj);
>>> +    struct hccs_dev *hdev = die->chip->hdev;
>>> +    u8 all_linked;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return sysfs_emit(buf, "%u\n", all_linked);
>>> +}
>>> +static struct kobj_attribute all_linked_on_die_attr =
>>> +        __ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
>>> +
>>> +static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
>>> +                        struct kobj_attribute *attr,
>>> +                        char *buf)
>>> +{
>>> +    const struct hccs_die_info *die = kobj_to_die_info(kobj);
>>> +    struct hccs_dev *hdev = die->chip->hdev;
>>> +    u8 full_lane;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return sysfs_emit(buf, "%u\n", full_lane);
>>> +}
>>> +static struct kobj_attribute linked_full_lane_on_die_attr =
>>> +    __ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, 
>>> NULL);
>>> +
>>> +static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
>>> +                       struct kobj_attribute *attr,
>>> +                       char *buf)
>>> +{
>>> +    const struct hccs_die_info *die = kobj_to_die_info(kobj);
>>> +    struct hccs_dev *hdev = die->chip->hdev;
>>> +    u64 total_crc_err_cnt;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    ret = hccs_get_die_total_crc_err_cnt(hdev, die, 
>>> &total_crc_err_cnt);
>>> +    mutex_unlock(&hdev->lock);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
>>> +}
>>> +static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
>>> +    __ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
>>> +
>>> +static struct attribute *hccs_die_default_attrs[] = {
>>> +    &all_linked_on_die_attr.attr,
>>> +    &linked_full_lane_on_die_attr.attr,
>>> +    &crc_err_cnt_sum_on_die_attr.attr,
>>> +    NULL,
>>> +};
>>> +ATTRIBUTE_GROUPS(hccs_die_default);
>>> +
>>> +static const struct kobj_type hccs_die_type = {
>>> +    .sysfs_ops = &hccs_comm_ops,
>>> +    .default_groups = hccs_die_default_groups,
>>> +};
>>> +
>>> +static ssize_t all_linked_on_chip_show(struct kobject *kobj,
>>> +                       struct kobj_attribute *attr, char *buf)
>>> +{
>>> +    const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
>>> +    struct hccs_dev *hdev = chip->hdev;
>>> +    const struct hccs_die_info *die;
>>> +    u8 all_linked = 1;
>>> +    u8 i, tmp;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    for (i = 0; i < chip->die_num; i++) {
>>> +        die = &chip->dies[i];
>>> +        ret = hccs_get_die_all_link_status(hdev, die, &tmp);
>>> +        if (ret) {
>>> +            mutex_unlock(&hdev->lock);
>>> +            return ret;
>>> +        }
>>> +        if (tmp != all_linked) {
>>> +            all_linked = 0;
>>> +            break;
>>> +        }
>>> +    }
>>> +    mutex_unlock(&hdev->lock);
>>> +
>>> +    return sysfs_emit(buf, "%u\n", all_linked);
>>> +}
>>> +static struct kobj_attribute all_linked_on_chip_attr =
>>> +        __ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
>>> +
>>> +static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
>>> +                         struct kobj_attribute *attr,
>>> +                         char *buf)
>>> +{
>>> +    const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
>>> +    struct hccs_dev *hdev = chip->hdev;
>>> +    const struct hccs_die_info *die;
>>> +    u8 full_lane = 1;
>>> +    u8 i, tmp;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    for (i = 0; i < chip->die_num; i++) {
>>> +        die = &chip->dies[i];
>>> +        ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
>>> +        if (ret) {
>>> +            mutex_unlock(&hdev->lock);
>>> +            return ret;
>>> +        }
>>> +        if (tmp != full_lane) {
>>> +            full_lane = 0;
>>> +            break;
>>> +        }
>>> +    }
>>> +    mutex_unlock(&hdev->lock);
>>> +
>>> +    return sysfs_emit(buf, "%u\n", full_lane);
>>> +}
>>> +static struct kobj_attribute linked_full_lane_on_chip_attr =
>>> +    __ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, 
>>> NULL);
>>> +
>>> +static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
>>> +                        struct kobj_attribute *attr,
>>> +                        char *buf)
>>> +{
>>> +    const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
>>> +    u64 crc_err_cnt, total_crc_err_cnt = 0;
>>> +    struct hccs_dev *hdev = chip->hdev;
>>> +    const struct hccs_die_info *die;
>>> +    int ret;
>>> +    u16 i;
>>> +
>>> +    mutex_lock(&hdev->lock);
>>> +    for (i = 0; i < chip->die_num; i++) {
>>> +        die = &chip->dies[i];
>>> +        ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
>>> +        if (ret) {
>>> +            mutex_unlock(&hdev->lock);
>>> +            return ret;
>>> +        }
>>> +
>>> +        total_crc_err_cnt += crc_err_cnt;
>>> +    }
>>> +    mutex_unlock(&hdev->lock);
>>> +
>>> +    return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
>>> +}
>>> +static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
>>> +        __ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
>>> +
>>> +static struct attribute *hccs_chip_default_attrs[] = {
>>> +    &all_linked_on_chip_attr.attr,
>>> +    &linked_full_lane_on_chip_attr.attr,
>>> +    &crc_err_cnt_sum_on_chip_attr.attr,
>>> +    NULL,
>>> +};
>>> +ATTRIBUTE_GROUPS(hccs_chip_default);
>>> +
>>> +static const struct kobj_type hccs_chip_type = {
>>> +    .sysfs_ops = &hccs_comm_ops,
>>> +    .default_groups = hccs_chip_default_groups,
>>> +};
>>> +
>>> +static void hccs_remove_die_dir(struct hccs_die_info *die)
>>> +{
>>> +    struct hccs_port_info *port;
>>> +    u8 i;
>>> +
>>> +    for (i = 0; i < die->port_num; i++) {
>>> +        port = &die->ports[i];
>>> +        if (port->dir_created)
>>> +            kobject_put(&port->kobj);
>>> +    }
>>> +
>>> +    kobject_put(&die->kobj);
>>> +}
>>> +
>>> +static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
>>> +{
>>> +    struct hccs_die_info *die;
>>> +    u8 i;
>>> +
>>> +    for (i = 0; i < chip->die_num; i++) {
>>> +        die = &chip->dies[i];
>>> +        if (die->dir_created)
>>> +            hccs_remove_die_dir(die);
>>> +    }
>>> +
>>> +    kobject_put(&chip->kobj);
>>> +}
>>> +
>>> +static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
>>> +{
>>> +    u8 i;
>>> +
>>> +    for (i = 0; i < hdev->chip_num; i++)
>>> +        hccs_remove_chip_dir(&hdev->chips[i]);
>>> +}
>>> +
>>> +static int hccs_create_hccs_dir(struct hccs_dev *hdev,
>>> +                struct hccs_die_info *die,
>>> +                struct hccs_port_info *port)
>>> +{
>>> +    int ret;
>>> +
>>> +    ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
>>> +                   &die->kobj, "hccs%d", port->port_id);
>>> +    if (ret) {
>>> +        kobject_put(&port->kobj);
>>> +        return ret;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hccs_create_die_dir(struct hccs_dev *hdev,
>>> +                   struct hccs_chip_info *chip,
>>> +                   struct hccs_die_info *die)
>>> +{
>>> +    struct hccs_port_info *port;
>>> +    int ret;
>>> +    u16 i;
>>> +
>>> +    ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
>>> +                   &chip->kobj, "die%d", die->die_id);
>>> +    if (ret) {
>>> +        kobject_put(&die->kobj);
>>> +        return ret;
>>> +    }
>>> +
>>> +    for (i = 0; i < die->port_num; i++) {
>>> +        port = &die->ports[i];
>>> +        ret = hccs_create_hccs_dir(hdev, die, port);
>>> +        if (ret) {
>>> +            dev_err(hdev->dev, "create hccs%d dir failed.\n",
>>> +                port->port_id);
>>> +            goto err;
>>> +        }
>>> +        port->dir_created = true;
>>> +    }
>>> +
>>> +    return 0;
>>> +err:
>>> +    hccs_remove_die_dir(die);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hccs_create_chip_dir(struct hccs_dev *hdev,
>>> +                struct hccs_chip_info *chip)
>>> +{
>>> +    struct hccs_die_info *die;
>>> +    int ret;
>>> +    u16 id;
>>> +
>>> +    ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
>>> +                   &hdev->dev->kobj, "chip%d", chip->chip_id);
>>> +    if (ret) {
>>> +        kobject_put(&chip->kobj);
>>> +        return ret;
>>> +    }
>>> +
>>> +    for (id = 0; id < chip->die_num; id++) {
>>> +        die = &chip->dies[id];
>>> +        ret = hccs_create_die_dir(hdev, chip, die);
>>> +        if (ret)
>>> +            goto err;
>>> +        die->dir_created = true;
>>> +    }
>>> +
>>> +    return 0;
>>> +err:
>>> +    hccs_remove_chip_dir(chip);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hccs_create_topo_dirs(struct hccs_dev *hdev)
>>> +{
>>> +    struct hccs_chip_info *chip;
>>> +    u8 id, k;
>>> +    int ret;
>>> +
>>> +    for (id = 0; id < hdev->chip_num; id++) {
>>> +        chip = &hdev->chips[id];
>>> +        ret = hccs_create_chip_dir(hdev, chip);
>>> +        if (ret) {
>>> +            dev_err(hdev->dev, "init chip%d dir failed!\n", id);
>>> +            goto err;
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +err:
>>> +    for (k = 0; k < id; k++)
>>> +        hccs_remove_chip_dir(&hdev->chips[k]);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hccs_probe(struct platform_device *pdev)
>>> +{
>>> +    struct acpi_device *acpi_dev;
>>> +    struct hccs_dev *hdev;
>>> +    int rc;
>>> +
>>> +    if (acpi_disabled) {
>>> +        dev_err(&pdev->dev, "acpi is disabled.\n");
>>> +        return -ENODEV;
>>> +    }
>>> +    acpi_dev = ACPI_COMPANION(&pdev->dev);
>>> +    if (!acpi_dev)
>>> +        return -ENODEV;
>>> +
>>> +    hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
>>> +    if (!hdev)
>>> +        return -ENOMEM;
>>> +    hdev->acpi_dev = acpi_dev;
>>> +    hdev->dev = &pdev->dev;
>>> +    platform_set_drvdata(pdev, hdev);
>>> +
>>> +    mutex_init(&hdev->lock);
>>> +    rc = hccs_get_pcc_chan_id(hdev);
>>> +    if (rc)
>>> +        return rc;
>>> +    rc = hccs_register_pcc_channel(hdev);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    rc = hccs_get_dev_caps(hdev);
>>> +    if (rc)
>>> +        goto unregister_pcc_chan;
>>> +
>>> +    rc = hccs_get_hw_info(hdev);
>>> +    if (rc)
>>> +        goto unregister_pcc_chan;
>>> +
>>> +    rc = hccs_create_topo_dirs(hdev);
>>> +    if (rc)
>>> +        goto unregister_pcc_chan;
>>> +
>>> +    return 0;
>>> +
>>> +unregister_pcc_chan:
>>> +    hccs_unregister_pcc_channel(hdev);
>>> +
>>> +    return rc;
>>> +}
>>> +
>>> +static int hccs_remove(struct platform_device *pdev)
>>> +{
>>> +    struct hccs_dev *hdev = platform_get_drvdata(pdev);
>>> +
>>> +    hccs_remove_topo_dirs(hdev);
>>> +    hccs_unregister_pcc_channel(hdev);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct acpi_device_id hccs_acpi_match[] = {
>>> +    { "HISI04B1", },
>>> +};
>> It is better to add MODULE_DEVICE_TABLE to autoload the driver.
> I tested that this macro doesn't help to autoload driver if select 
> this driver as 'M'.
> How does it work?
I know how it works. It does automatically load the driver even if 
select 'M'.
will add it in v4.
>
> Best regards,
> Huisong
>>
> [snip]
> .

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v4 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (4 preceding siblings ...)
  2023-07-25  7:57 ` [PATCH RESEND " Huisong Li
@ 2023-07-28  3:03 ` Huisong Li
  2023-07-28  3:03   ` [PATCH v4 1/2] " Huisong Li
  2023-07-28  3:03   ` [PATCH v4 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  2023-07-29  8:26 ` [PATCH v5 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-28  3:03 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v4:
 - remove useless header and reorder linux header.
 - use __ATTR_RO to replace __ATTR for port attributes.
 - add MODULE_DEVICE_TABLE to autoload the driver.
 - update the date to "November 2023".
 - fix some comments about HCCS description.

 v3:
  - replace "using_status" with "enable" attribute.
  - fix some comments in codes.

 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (2):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   76 +
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   20 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1282 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
 8 files changed, 1585 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v4 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-28  3:03 ` [PATCH v4 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-07-28  3:03   ` Huisong Li
  2023-07-28  3:03   ` [PATCH v4 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  1 sibling, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-28  3:03 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
bus protocol. The performance of the application may be affected if some
hccs ports on platform are not in full lane status, have a large number
of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   20 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1282 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
 7 files changed, 1508 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d73c9e97a237..c9e302c3ff48 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9488,6 +9488,12 @@ S:	Maintained
 F:	Documentation/ABI/testing/debugfs-hisi-zip
 F:	drivers/crypto/hisilicon/zip/
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HMM - Heterogeneous Memory Management
 M:	Jérôme Glisse <jglisse@redhat.com>
 L:	linux-mm@kvack.org
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 708eaad8f590..0706a27d13be 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..73a99b8156c9
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache Coherence System (HCCS) is a multi-chip
+	  interconnection bus protocol.
+	  The performance of application may be affected if some hccs
+	  ports are not in full lane status, have a large number of CRC
+	  errors and so on.
+
+	  Say M here if you want to include support for querying the
+	  health status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..c4c45ab3f106
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
+ * bus protocol.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve the following information about each port:
+ *    - port type
+ *    - lane mode
+ *    - enable
+ *    - current lane mode
+ *    - link state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve the following information about all the ports on the chip or
+ *   the die:
+ *    - if all enabled ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/acpi.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->enable = attrs[i].enable;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr = __ATTR_RO(type);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode);
+
+static ssize_t enable_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->enable);
+}
+
+static struct kobj_attribute port_enable_attr = __ATTR_RO(enable);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr = __ATTR_RO(cur_lane_num);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr = __ATTR_RO(link_fsm);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr = __ATTR_RO(lane_mask);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr = __ATTR_RO(crc_err_cnt);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&port_enable_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1"},
+	{ ""},
+};
+MODULE_DEVICE_TABLE(acpi, hccs_acpi_match);
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..f9a79904d9c7
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool enable; /* if the port is enabled */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 enable : 1; /* if the port is enabled */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v4 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-07-28  3:03 ` [PATCH v4 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-07-28  3:03   ` [PATCH v4 1/2] " Huisong Li
@ 2023-07-28  3:03   ` Huisong Li
  1 sibling, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-28  3:03 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 76 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 77 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..70b2618f1869
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,76 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this chip are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this die are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		============= ==== =============================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+
+		The HCCS port have the following attributes:
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		enable:       (RO) indicate if this port is enabled (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
+		Note: type, lane_mode and enable are fixed attributes on
+		      running platform.
diff --git a/MAINTAINERS b/MAINTAINERS
index c9e302c3ff48..764b20b1d19a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9491,6 +9491,7 @@ F:	drivers/crypto/hisilicon/zip/
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v5 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (5 preceding siblings ...)
  2023-07-28  3:03 ` [PATCH v4 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-07-29  8:26 ` Huisong Li
  2023-07-29  8:26   ` [PATCH v5 1/2] " Huisong Li
  2023-07-29  8:26   ` [PATCH v5 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  2023-08-01  2:41 ` [PATCH v6 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  8 siblings, 2 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-29  8:26 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v5:
 - fix document format to eliminate warning of making htmldocs.

 v4:
 - remove useless header and reorder linux header.
 - use __ATTR_RO to replace __ATTR for port attributes.
 - add MODULE_DEVICE_TABLE to autoload the driver.
 - update the date to "November 2023".
 - fix some comments about HCCS description.

 v3:
  - replace "using_status" with "enable" attribute.
  - fix some comments in codes.

 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (2):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   81 ++
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   20 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1282 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
 8 files changed, 1590 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v5 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-29  8:26 ` [PATCH v5 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-07-29  8:26   ` Huisong Li
  2023-07-29 22:43     ` Randy Dunlap
  2023-07-29  8:26   ` [PATCH v5 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  1 sibling, 1 reply; 79+ messages in thread
From: Huisong Li @ 2023-07-29  8:26 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
bus protocol. The performance of the application may be affected if some
hccs ports on platform are not in full lane status, have a large number
of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   20 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1282 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
 7 files changed, 1508 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d73c9e97a237..c9e302c3ff48 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9488,6 +9488,12 @@ S:	Maintained
 F:	Documentation/ABI/testing/debugfs-hisi-zip
 F:	drivers/crypto/hisilicon/zip/
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HMM - Heterogeneous Memory Management
 M:	Jérôme Glisse <jglisse@redhat.com>
 L:	linux-mm@kvack.org
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 708eaad8f590..0706a27d13be 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..73a99b8156c9
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache Coherence System (HCCS) is a multi-chip
+	  interconnection bus protocol.
+	  The performance of application may be affected if some hccs
+	  ports are not in full lane status, have a large number of CRC
+	  errors and so on.
+
+	  Say M here if you want to include support for querying the
+	  health status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..74d351bc0573
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
+ * bus protocol.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve the following information about each port:
+ *    - port type
+ *    - lane mode
+ *    - enable
+ *    - current lane mode
+ *    - link finite state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve the following information about all the ports on the chip or
+ *   the die:
+ *    - if all enabled ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/acpi.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->enable = attrs[i].enable;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr = __ATTR_RO(type);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode);
+
+static ssize_t enable_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->enable);
+}
+
+static struct kobj_attribute port_enable_attr = __ATTR_RO(enable);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr = __ATTR_RO(cur_lane_num);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr = __ATTR_RO(link_fsm);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr = __ATTR_RO(lane_mask);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr = __ATTR_RO(crc_err_cnt);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&port_enable_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1"},
+	{ ""},
+};
+MODULE_DEVICE_TABLE(acpi, hccs_acpi_match);
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..f9a79904d9c7
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool enable; /* if the port is enabled */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 enable : 1; /* if the port is enabled */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v5 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-07-29  8:26 ` [PATCH v5 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-07-29  8:26   ` [PATCH v5 1/2] " Huisong Li
@ 2023-07-29  8:26   ` Huisong Li
  1 sibling, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-07-29  8:26 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 81 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 82 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..fdb4e36310fb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,81 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+What:		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+What:		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this chip are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		================= ==== =========================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this die are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		================= ==== =========================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/link_fsm
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+		Note: type, lane_mode and enable are fixed attributes on running
+		platform.
+
+		The HCCS port have the following attributes:
+
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		enable:       (RO) indicate if this port is enabled (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		link_fsm:     (RO) link finite state machine of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
diff --git a/MAINTAINERS b/MAINTAINERS
index c9e302c3ff48..764b20b1d19a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9491,6 +9491,7 @@ F:	drivers/crypto/hisilicon/zip/
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH v5 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-29  8:26   ` [PATCH v5 1/2] " Huisong Li
@ 2023-07-29 22:43     ` Randy Dunlap
  2023-08-01  1:30       ` lihuisong (C)
  0 siblings, 1 reply; 79+ messages in thread
From: Randy Dunlap @ 2023-07-29 22:43 UTC (permalink / raw)
  To: Huisong Li, xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong

Hi--

On 7/29/23 01:26, Huisong Li wrote:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d73c9e97a237..c9e302c3ff48 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9488,6 +9488,12 @@ S:	Maintained
>  F:	Documentation/ABI/testing/debugfs-hisi-zip
>  F:	drivers/crypto/hisilicon/zip/
>  

This new entry should be between
HISILICON I2C CONTROLLER DRIVER
and
HISILICON LPC BUS DRIVER
to keep the MAINTAINERS file in alphabetical order.

> +HISILICON KUNPENG SOC HCCS DRIVER
> +M:	Huisong Li <lihuisong@huawei.com>
> +S:	Maintained
> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
> +
>  HMM - Heterogeneous Memory Management
>  M:	Jérôme Glisse <jglisse@redhat.com>
>  L:	linux-mm@kvack.org

> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
> new file mode 100644
> index 000000000000..73a99b8156c9
> --- /dev/null
> +++ b/drivers/soc/hisilicon/Kconfig
> @@ -0,0 +1,20 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Hisilicon SoC drivers"
> +	depends on ARCH_HISI || COMPILE_TEST
> +
> +config KUNPENG_HCCS
> +	tristate "HCCS driver on Kunpeng SoC"
> +	depends on ACPI
> +	depends on ARM64 || COMPILE_TEST
> +	help
> +	  The Huawei Cache Coherence System (HCCS) is a multi-chip
> +	  interconnection bus protocol.
> +	  The performance of application may be affected if some hccs

s/hccs/HCCS/ for consistency.

> +	  ports are not in full lane status, have a large number of CRC
> +	  errors and so on.
> +
> +	  Say M here if you want to include support for querying the
> +	  health status and port information of HCCS on Kunpeng SoC.

thanks.

-- 
~Randy

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v5 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-07-29 22:43     ` Randy Dunlap
@ 2023-08-01  1:30       ` lihuisong (C)
  0 siblings, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-08-01  1:30 UTC (permalink / raw)
  To: Randy Dunlap, xuwei5, arnd, krzk, sudeep.holla
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong


在 2023/7/30 6:43, Randy Dunlap 写道:
> Hi--
>
> On 7/29/23 01:26, Huisong Li wrote:
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index d73c9e97a237..c9e302c3ff48 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9488,6 +9488,12 @@ S:	Maintained
>>   F:	Documentation/ABI/testing/debugfs-hisi-zip
>>   F:	drivers/crypto/hisilicon/zip/
>>   
> This new entry should be between
> HISILICON I2C CONTROLLER DRIVER
> and
> HISILICON LPC BUS DRIVER
> to keep the MAINTAINERS file in alphabetical order.
ok, will fix in v6. thanks.
>> +HISILICON KUNPENG SOC HCCS DRIVER
>> +M:	Huisong Li <lihuisong@huawei.com>
>> +S:	Maintained
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.c
>> +F:	drivers/soc/hisilicon/kunpeng_hccs.h
>> +
>>   HMM - Heterogeneous Memory Management
>>   M:	Jérôme Glisse <jglisse@redhat.com>
>>   L:	linux-mm@kvack.org
>> diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
>> new file mode 100644
>> index 000000000000..73a99b8156c9
>> --- /dev/null
>> +++ b/drivers/soc/hisilicon/Kconfig
>> @@ -0,0 +1,20 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +
>> +menu "Hisilicon SoC drivers"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +
>> +config KUNPENG_HCCS
>> +	tristate "HCCS driver on Kunpeng SoC"
>> +	depends on ACPI
>> +	depends on ARM64 || COMPILE_TEST
>> +	help
>> +	  The Huawei Cache Coherence System (HCCS) is a multi-chip
>> +	  interconnection bus protocol.
>> +	  The performance of application may be affected if some hccs
> s/hccs/HCCS/ for consistency.
Ack
>
>> +	  ports are not in full lane status, have a large number of CRC
>> +	  errors and so on.
>> +
>> +	  Say M here if you want to include support for querying the
>> +	  health status and port information of HCCS on Kunpeng SoC.
> thanks.
>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v6 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (6 preceding siblings ...)
  2023-07-29  8:26 ` [PATCH v5 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-08-01  2:41 ` Huisong Li
  2023-08-01  2:41   ` [PATCH v6 1/2] " Huisong Li
  2023-08-01  2:41   ` [PATCH v6 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  8 siblings, 2 replies; 79+ messages in thread
From: Huisong Li @ 2023-08-01  2:41 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v6:
 - fix the new entry in MAINTAINERS file to keep alphabetical order

 v5:
 - fix document format to eliminate warning of making htmldocs.

 v4:
 - remove useless header and reorder linux header.
 - use __ATTR_RO to replace __ATTR for port attributes.
 - add MODULE_DEVICE_TABLE to autoload the driver.
 - update the date to "November 2023".
 - fix some comments about HCCS description.

 v3:
  - replace "using_status" with "enable" attribute.
  - fix some comments in codes.

 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (2):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   81 ++
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   20 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1282 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  196 +++
 8 files changed, 1590 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v6 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-08-01  2:41 ` [PATCH v6 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-08-01  2:41   ` Huisong Li
  2023-08-06 15:09     ` Zenghui Yu
  2023-08-01  2:41   ` [PATCH v6 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  1 sibling, 1 reply; 79+ messages in thread
From: Huisong Li @ 2023-08-01  2:41 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
bus protocol. The performance of the application may be affected if some
HCCS ports on platform are not in full lane status, have a large number
of CRC errors and so on.

This driver provides the query interface of the health status and
port information of HCCS on Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |    6 +
 drivers/soc/Kconfig                  |    1 +
 drivers/soc/Makefile                 |    1 +
 drivers/soc/hisilicon/Kconfig        |   20 +
 drivers/soc/hisilicon/Makefile       |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 1282 ++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  196 ++++
 7 files changed, 1508 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d7b867c57920..082e0c18a49a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9372,6 +9372,12 @@ W:	https://www.hisilicon.com
 F:	Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml
 F:	drivers/i2c/busses/i2c-hisi.c
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HISILICON LPC BUS DRIVER
 M:	Jay Fang <f.fangjian@huawei.com>
 S:	Maintained
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 708eaad8f590..0706a27d13be 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..7c84dd4da0f8
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache Coherence System (HCCS) is a multi-chip
+	  interconnection bus protocol.
+	  The performance of application may be affected if some HCCS
+	  ports are not in full lane status, have a large number of CRC
+	  errors and so on.
+
+	  Say M here if you want to include support for querying the
+	  health status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..74d351bc0573
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
+ * bus protocol.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve the following information about each port:
+ *    - port type
+ *    - lane mode
+ *    - enable
+ *    - current lane mode
+ *    - link finite state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve the following information about all the ports on the chip or
+ *   the die:
+ *    - if all enabled ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
+ */
+#include <linux/acpi.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->enable = attrs[i].enable;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",
+					die->die_id, i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform  failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+
+static struct kobj_attribute hccs_type_attr = __ATTR_RO(type);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+
+static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode);
+
+static ssize_t enable_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->enable);
+}
+
+static struct kobj_attribute port_enable_attr = __ATTR_RO(enable);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+
+static struct kobj_attribute cur_lane_num_attr = __ATTR_RO(cur_lane_num);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+
+static struct kobj_attribute link_fsm_attr = __ATTR_RO(link_fsm);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+
+static struct kobj_attribute lane_mask_attr = __ATTR_RO(lane_mask);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+
+static struct kobj_attribute crc_err_cnt_attr = __ATTR_RO(crc_err_cnt);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&port_enable_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_remove_topo_dirs(hdev);
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1"},
+	{ ""},
+};
+MODULE_DEVICE_TABLE(acpi, hccs_acpi_match);
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..f9a79904d9c7
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool enable; /* if the port is enabled */
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct kobject kobj;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 enable : 1; /* if the port is enabled */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the all information of HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#define hccs_get_field(origin, mask, shift) \
+	(((origin) & (mask)) >> (shift))
+#define hccs_get_bit(origin, shift) \
+	hccs_get_field((origin), (0x1UL << (shift)), (shift))
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v6 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-08-01  2:41 ` [PATCH v6 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-08-01  2:41   ` [PATCH v6 1/2] " Huisong Li
@ 2023-08-01  2:41   ` Huisong Li
  1 sibling, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-08-01  2:41 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 81 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 82 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..fdb4e36310fb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,81 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+What:		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+What:		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this chip are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		================= ==== =========================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this die are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		================= ==== =========================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/link_fsm
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+		Note: type, lane_mode and enable are fixed attributes on running
+		platform.
+
+		The HCCS port have the following attributes:
+
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		enable:       (RO) indicate if this port is enabled (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		link_fsm:     (RO) link finite state machine of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
diff --git a/MAINTAINERS b/MAINTAINERS
index 082e0c18a49a..53df506772dc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9375,6 +9375,7 @@ F:	drivers/i2c/busses/i2c-hisi.c
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH v6 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-08-01  2:41   ` [PATCH v6 1/2] " Huisong Li
@ 2023-08-06 15:09     ` Zenghui Yu
  2023-08-07  1:14       ` lihuisong (C)
  2023-08-07  1:41       ` lihuisong (C)
  0 siblings, 2 replies; 79+ messages in thread
From: Zenghui Yu @ 2023-08-06 15:09 UTC (permalink / raw)
  To: Huisong Li, xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong

A few trivial comments inline.

On 2023/8/1 10:41, Huisong Li wrote:
> The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
> bus protocol. The performance of the application may be affected if some
> HCCS ports on platform are not in full lane status, have a large number
> of CRC errors and so on.
> 
> This driver provides the query interface of the health status and
> port information of HCCS on Kunpeng SoC.
> 
> Signed-off-by: Huisong Li <lihuisong@huawei.com>

[...]

> +static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
> +{
> +
> +	struct device *dev = hdev->dev;
> +	struct hccs_chip_info *chip;
> +	struct hccs_die_info *die;
> +	u8 i, j;
> +	int ret;
> +
> +	for (i = 0; i < hdev->chip_num; i++) {
> +		chip = &hdev->chips[i];
> +		for (j = 0; j < chip->die_num; j++) {
> +			die = &chip->dies[j];
> +			if (!die->port_num)
> +				continue;
> +
> +			die->ports = devm_kzalloc(dev,
> +				die->port_num * sizeof(struct hccs_port_info),
> +				GFP_KERNEL);
> +			if (!die->ports) {
> +				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
> +					i, die->die_id);
> +				return -ENOMEM;
> +			}
> +
> +			ret = hccs_get_all_port_info_on_die(hdev, die);
> +			if (ret) {
> +				dev_err(dev, "get die(%u) info on chip%u failed, ret = %d.\n",

"get *port* info failed"?

> +static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
> +					const struct hccs_die_info *die,
> +					u8 *all_linked)
> +{
> +	struct hccs_die_comm_req_param *req_param;
> +	struct hccs_desc desc;
> +	int ret;
> +
> +	if (die->port_num == 0) {
> +		*all_linked = 1;
> +		return 0;
> +	}
> +
> +	hccs_init_req_desc(&desc);
> +	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
> +	req_param->chip_id = die->chip->chip_id;
> +	req_param->die_id = die->die_id;
> +	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);

Typo? Looks like we intend to send a HCCS_GET_DIE_PORTS_LINK_STA
command.

> +/*
> + * This value cannot be 255, otherwise the loop of the multi-BD communication
> + * case cannot end.
> + */
> +#define HCCS_DIE_MAX_PORT_ID	254

This looks weird. Isn't the "max port id" depends on your HW
implementation?

> +#define hccs_get_field(origin, mask, shift) \
> +	(((origin) & (mask)) >> (shift))
> +#define hccs_get_bit(origin, shift) \
> +	hccs_get_field((origin), (0x1UL << (shift)), (shift))

Unused macroes.

P.S., I'd personally prefer splitting this patch in 2 to ease other
reviewer's work:

- deal with the HCCS HW (chip/die/port) probing
- focus on the sysfs/query things

Zenghui

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v6 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-08-06 15:09     ` Zenghui Yu
@ 2023-08-07  1:14       ` lihuisong (C)
  2023-08-07  1:41       ` lihuisong (C)
  1 sibling, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-08-07  1:14 UTC (permalink / raw)
  To: Zenghui Yu, xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong


在 2023/8/6 23:09, Zenghui Yu 写道:
> A few trivial comments inline.

Hi Zenghui,

Many thanks for reviewing my patch carefully.😁

>
> On 2023/8/1 10:41, Huisong Li wrote:
>> The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
>> bus protocol. The performance of the application may be affected if some
>> HCCS ports on platform are not in full lane status, have a large number
>> of CRC errors and so on.
>>
>> This driver provides the query interface of the health status and
>> port information of HCCS on Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>
> [...]
>
>> +static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
>> +{
>> +
>> +    struct device *dev = hdev->dev;
>> +    struct hccs_chip_info *chip;
>> +    struct hccs_die_info *die;
>> +    u8 i, j;
>> +    int ret;
>> +
>> +    for (i = 0; i < hdev->chip_num; i++) {
>> +        chip = &hdev->chips[i];
>> +        for (j = 0; j < chip->die_num; j++) {
>> +            die = &chip->dies[j];
>> +            if (!die->port_num)
>> +                continue;
>> +
>> +            die->ports = devm_kzalloc(dev,
>> +                die->port_num * sizeof(struct hccs_port_info),
>> +                GFP_KERNEL);
>> +            if (!die->ports) {
>> +                dev_err(dev, "allocate ports memory on chip%u/die%u 
>> failed.\n",
>> +                    i, die->die_id);
>> +                return -ENOMEM;
>> +            }
>> +
>> +            ret = hccs_get_all_port_info_on_die(hdev, die);
>> +            if (ret) {
>> +                dev_err(dev, "get die(%u) info on chip%u failed, ret 
>> = %d.\n",
>
> "get *port* info failed"?
Yes, this is more exact.
Will be fixed to "get port info on chip%u/die%u failed"
>
>> +static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
>> +                    const struct hccs_die_info *die,
>> +                    u8 *all_linked)
>> +{
>> +    struct hccs_die_comm_req_param *req_param;
>> +    struct hccs_desc desc;
>> +    int ret;
>> +
>> +    if (die->port_num == 0) {
>> +        *all_linked = 1;
>> +        return 0;
>> +    }
>> +
>> +    hccs_init_req_desc(&desc);
>> +    req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>> +    req_param->chip_id = die->chip->chip_id;
>> +    req_param->die_id = die->die_id;
>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
>
> Typo? Looks like we intend to send a HCCS_GET_DIE_PORTS_LINK_STA
> command.

Yes, you are right. It's my fault.

Appreciate you so much for pointing it out.

I will also check other commands again.

>
>> +/*
>> + * This value cannot be 255, otherwise the loop of the multi-BD 
>> communication
>> + * case cannot end.
>> + */
>> +#define HCCS_DIE_MAX_PORT_ID    254
>
> This looks weird. Isn't the "max port id" depends on your HW
> implementation?
>
>> +#define hccs_get_field(origin, mask, shift) \
>> +    (((origin) & (mask)) >> (shift))
>> +#define hccs_get_bit(origin, shift) \
>> +    hccs_get_field((origin), (0x1UL << (shift)), (shift))
>
> Unused macroes.
>
> P.S., I'd personally prefer splitting this patch in 2 to ease other
> reviewer's work:
>
> - deal with the HCCS HW (chip/die/port) probing
> - focus on the sysfs/query things
>
> Zenghui
> .

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH v6 1/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-08-06 15:09     ` Zenghui Yu
  2023-08-07  1:14       ` lihuisong (C)
@ 2023-08-07  1:41       ` lihuisong (C)
  1 sibling, 0 replies; 79+ messages in thread
From: lihuisong (C) @ 2023-08-07  1:41 UTC (permalink / raw)
  To: Zenghui Yu, xuwei5, arnd, krzk, sudeep.holla, rdunlap
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Hi Zenghui,

Please ignore the previous email and take a look at this.


在 2023/8/6 23:09, Zenghui Yu 写道:
> A few trivial comments inline.
Many thanks for reviewing my patch carefully.😁
>
> On 2023/8/1 10:41, Huisong Li wrote:
>> The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
>> bus protocol. The performance of the application may be affected if some
>> HCCS ports on platform are not in full lane status, have a large number
>> of CRC errors and so on.
>>
>> This driver provides the query interface of the health status and
>> port information of HCCS on Kunpeng SoC.
>>
>> Signed-off-by: Huisong Li <lihuisong@huawei.com>
>
> [...]
>
>> +static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
>> +{
>> +
>> +    struct device *dev = hdev->dev;
>> +    struct hccs_chip_info *chip;
>> +    struct hccs_die_info *die;
>> +    u8 i, j;
>> +    int ret;
>> +
>> +    for (i = 0; i < hdev->chip_num; i++) {
>> +        chip = &hdev->chips[i];
>> +        for (j = 0; j < chip->die_num; j++) {
>> +            die = &chip->dies[j];
>> +            if (!die->port_num)
>> +                continue;
>> +
>> +            die->ports = devm_kzalloc(dev,
>> +                die->port_num * sizeof(struct hccs_port_info),
>> +                GFP_KERNEL);
>> +            if (!die->ports) {
>> +                dev_err(dev, "allocate ports memory on chip%u/die%u 
>> failed.\n",
>> +                    i, die->die_id);
>> +                return -ENOMEM;
>> +            }
>> +
>> +            ret = hccs_get_all_port_info_on_die(hdev, die);
>> +            if (ret) {
>> +                dev_err(dev, "get die(%u) info on chip%u failed, ret 
>> = %d.\n",
>
> "get *port* info failed"?
Yes, this is more exact.
Will be fixed to "get port info on chip%u/die%u failed".
>
>> +static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
>> +                    const struct hccs_die_info *die,
>> +                    u8 *all_linked)
>> +{
>> +    struct hccs_die_comm_req_param *req_param;
>> +    struct hccs_desc desc;
>> +    int ret;
>> +
>> +    if (die->port_num == 0) {
>> +        *all_linked = 1;
>> +        return 0;
>> +    }
>> +
>> +    hccs_init_req_desc(&desc);
>> +    req_param = (struct hccs_die_comm_req_param *)desc.req.data;
>> +    req_param->chip_id = die->chip->chip_id;
>> +    req_param->die_id = die->die_id;
>> +    ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
>
> Typo? Looks like we intend to send a HCCS_GET_DIE_PORTS_LINK_STA
> command.
Yes, you are right. It's my fault.
Appreciate you so much for pointing it out.
I will also check other commands again.
>
>> +/*
>> + * This value cannot be 255, otherwise the loop of the multi-BD 
>> communication
>> + * case cannot end.
>> + */
>> +#define HCCS_DIE_MAX_PORT_ID    254
>
> This looks weird. Isn't the "max port id" depends on your HW
> implementation?
The "max port id" normally depends on HW implementation.
And there are no so many numbers of port on one die.
But HW implementation specification is possiable to change in furture SoC.
Driver should be compatible with it.
So "max port id" here comes from the communication interface and way 
with firmware.
>
>> +#define hccs_get_field(origin, mask, shift) \
>> +    (((origin) & (mask)) >> (shift))
>> +#define hccs_get_bit(origin, shift) \
>> +    hccs_get_field((origin), (0x1UL << (shift)), (shift))
>
> Unused macroes.
This macroes were used in previous version.
Later, the place where it is used was deleted, now it is unused indeed.
will delete it.
>
> P.S., I'd personally prefer splitting this patch in 2 to ease other
> reviewer's work:
>
> - deal with the HCCS HW (chip/die/port) probing
> - focus on the sysfs/query things
Ok, I will split this patch in next version. Thanks.
> .
/Huisong

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                   ` (7 preceding siblings ...)
  2023-08-01  2:41 ` [PATCH v6 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-08-08  2:36 ` Huisong Li
  2023-08-08  2:36   ` [PATCH v7 1/3] " Huisong Li
                     ` (3 more replies)
  8 siblings, 4 replies; 79+ messages in thread
From: Huisong Li @ 2023-08-08  2:36 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap, zenghui.yu
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

This series add HCCS driver to query the health status and port information
of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
this driver.

---
 v7:
 - split patch 1/2 to make it easier to review.                 
 - fix a wrong command code in hccs_get_die_all_link_status(). 
 - remove unused code and fix a log description in error branch.

 v6:
 - fix the new entry in MAINTAINERS file to keep alphabetical order

 v5:
 - fix document format to eliminate warning of making htmldocs.

 v4:
 - remove useless header and reorder linux header.
 - use __ATTR_RO to replace __ATTR for port attributes.
 - add MODULE_DEVICE_TABLE to autoload the driver.
 - update the date to "November 2023".
 - fix some comments about HCCS description.

 v3:
  - replace "using_status" with "enable" attribute.
  - fix some comments in codes.

 v2:
  - Document all sysfs entries provided by driver.
  - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
  - using _CRS with PCC GAS to get channel ID instead of _DSD.
  - replace readw_relaxed_poll_timeout with readw_poll_timeout.
  - use sysfs_emit() instead of sprintf().
  - drop ACPI_PTR in hccs_driver.
  - drop useless log during the probe phase.

Huisong Li (3):
  soc: hisilicon: Support HCCS driver on Kunpeng SoC
  soc: hisilicon: add sysfs entry to query information of HCCS
  doc: soc: hisilicon: Add Kunpeng HCCS driver documentation

 .../sysfs-devices-platform-kunpeng_hccs       |   81 ++
 MAINTAINERS                                   |    7 +
 drivers/soc/Kconfig                           |    1 +
 drivers/soc/Makefile                          |    1 +
 drivers/soc/hisilicon/Kconfig                 |   20 +
 drivers/soc/hisilicon/Makefile                |    2 +
 drivers/soc/hisilicon/kunpeng_hccs.c          | 1275 +++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h          |  191 +++
 8 files changed, 1578 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH v7 1/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
@ 2023-08-08  2:36   ` Huisong Li
  2023-08-08  2:36   ` [PATCH v7 2/3] soc: hisilicon: add sysfs entry to query information of HCCS Huisong Li
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-08-08  2:36 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap, zenghui.yu
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
bus protocol. This driver is aimed to support some features about HCCS on
Kunpeng SoC, like, querying the health status of HCCS.

This patch adds the probing of HCCS driver, and obtains all HCCS port
information by the dimension of chip and die on platform.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 MAINTAINERS                          |   6 +
 drivers/soc/Kconfig                  |   1 +
 drivers/soc/Makefile                 |   1 +
 drivers/soc/hisilicon/Kconfig        |  20 +
 drivers/soc/hisilicon/Makefile       |   2 +
 drivers/soc/hisilicon/kunpeng_hccs.c | 637 +++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h | 169 +++++++
 7 files changed, 836 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
 create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d48d8e857f57..6d1ec10da20e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9375,6 +9375,12 @@ W:	https://www.hisilicon.com
 F:	Documentation/devicetree/bindings/i2c/hisilicon,ascend910-i2c.yaml
 F:	drivers/i2c/busses/i2c-hisi.c
 
+HISILICON KUNPENG SOC HCCS DRIVER
+M:	Huisong Li <lihuisong@huawei.com>
+S:	Maintained
+F:	drivers/soc/hisilicon/kunpeng_hccs.c
+F:	drivers/soc/hisilicon/kunpeng_hccs.h
+
 HISILICON LPC BUS DRIVER
 M:	Jay Fang <f.fangjian@huawei.com>
 S:	Maintained
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 4e176280113a..d21e75d69294 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/fujitsu/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
 source "drivers/soc/litex/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 708eaad8f590..0706a27d13be 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
 obj-y				+= fujitsu/
 obj-$(CONFIG_ARCH_GEMINI)	+= gemini/
+obj-y				+= hisilicon/
 obj-y				+= imx/
 obj-y				+= ixp4xx/
 obj-$(CONFIG_SOC_XWAY)		+= lantiq/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..7c84dd4da0f8
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Hisilicon SoC drivers"
+	depends on ARCH_HISI || COMPILE_TEST
+
+config KUNPENG_HCCS
+	tristate "HCCS driver on Kunpeng SoC"
+	depends on ACPI
+	depends on ARM64 || COMPILE_TEST
+	help
+	  The Huawei Cache Coherence System (HCCS) is a multi-chip
+	  interconnection bus protocol.
+	  The performance of application may be affected if some HCCS
+	  ports are not in full lane status, have a large number of CRC
+	  errors and so on.
+
+	  Say M here if you want to include support for querying the
+	  health status and port information of HCCS on Kunpeng SoC.
+
+endmenu
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..226e747e70d6
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_KUNPENG_HCCS)	+= kunpeng_hccs.o
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
new file mode 100644
index 000000000000..b61de32ce00c
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -0,0 +1,637 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection
+ * bus protocol.
+ *
+ * Copyright (c) 2023 Hisilicon Limited.
+ * Author: Huisong Li <lihuisong@huawei.com>
+ */
+#include <linux/acpi.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+
+#include <acpi/pcc.h>
+
+#include "kunpeng_hccs.h"
+
+/* PCC defines */
+#define HCCS_PCC_SIGNATURE_MASK		0x50434300
+#define HCCS_PCC_STATUS_CMD_COMPLETE	BIT(0)
+
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
+#define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
+
+struct hccs_register_ctx {
+	struct device *dev;
+	u8 chan_id;
+	int err;
+};
+
+static acpi_status hccs_get_register_cb(struct acpi_resource *ares,
+					void *context)
+{
+	struct acpi_resource_generic_register *reg;
+	struct hccs_register_ctx *ctx = context;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+		return AE_OK;
+
+	reg = &ares->data.generic_reg;
+	if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) {
+		dev_err(ctx->dev, "Bad register resource.\n");
+		ctx->err = -EINVAL;
+		return AE_ERROR;
+	}
+	ctx->chan_id = reg->access_size;
+
+	return AE_OK;
+}
+
+static int hccs_get_pcc_chan_id(struct hccs_dev *hdev)
+{
+	acpi_handle handle = ACPI_HANDLE(hdev->dev);
+	struct hccs_register_ctx ctx = {0};
+	acpi_status status;
+
+	if (!acpi_has_method(handle, METHOD_NAME__CRS))
+		return -ENODEV;
+
+	ctx.dev = hdev->dev;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     hccs_get_register_cb, &ctx);
+	if (ACPI_FAILURE(status))
+		return ctx.err;
+	hdev->chan_id = ctx.chan_id;
+
+	return 0;
+}
+
+static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret < 0)
+		pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+	else
+		pr_debug("TX completed. CMD sent:0x%x, ret:%d\n",
+			 *(u8 *)msg, ret);
+}
+
+static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+
+	if (cl_info->pcc_comm_addr)
+		iounmap(cl_info->pcc_comm_addr);
+	pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
+}
+
+static int hccs_register_pcc_channel(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct mbox_client *cl = &cl_info->client;
+	struct pcc_mbox_chan *pcc_chan;
+	struct device *dev = hdev->dev;
+	int rc;
+
+	cl->dev = dev;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+	cl->tx_done = hccs_chan_tx_done;
+	pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
+	if (IS_ERR(pcc_chan)) {
+		dev_err(dev, "PPC channel request failed.\n");
+		rc = -ENODEV;
+		goto out;
+	}
+	cl_info->pcc_chan = pcc_chan;
+	cl_info->mbox_chan = pcc_chan->mchan;
+
+	/*
+	 * pcc_chan->latency is just a nominal value. In reality the remote
+	 * processor could be much slower to reply. So add an arbitrary amount
+	 * of wait on top of nominal.
+	 */
+	cl_info->deadline_us =
+			HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
+	if (cl_info->mbox_chan->mbox->txdone_irq) {
+		dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
+		rc = -EINVAL;
+		goto err_mbx_channel_free;
+	}
+
+	if (pcc_chan->shmem_base_addr) {
+		cl_info->pcc_comm_addr = (void __force *)ioremap(
+			pcc_chan->shmem_base_addr, pcc_chan->shmem_size);
+		if (!cl_info->pcc_comm_addr) {
+			dev_err(dev, "Failed to ioremap PCC communication region for channel-%d.\n",
+				hdev->chan_id);
+			rc = -ENOMEM;
+			goto err_mbx_channel_free;
+		}
+	}
+
+	return 0;
+
+err_mbx_channel_free:
+	pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
+	return rc;
+}
+
+static int hccs_check_chan_cmd_complete(struct hccs_dev *hdev)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	u16 status;
+	int ret;
+
+	/*
+	 * Poll PCC status register every 3us(delay_us) for maximum of
+	 * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+	 */
+	ret = readw_poll_timeout(&comm_base->status, status,
+				 status & HCCS_PCC_STATUS_CMD_COMPLETE,
+				 HCCS_POLL_STATUS_TIME_INTERVAL_US,
+				 cl_info->deadline_us);
+	if (unlikely(ret))
+		dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret);
+
+	return ret;
+}
+
+static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
+			     struct hccs_desc *desc)
+{
+	struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
+	struct acpi_pcct_shared_memory *comm_base = cl_info->pcc_comm_addr;
+	void *comm_space = (void *)(comm_base + 1);
+	struct hccs_fw_inner_head *fw_inner_head;
+	struct acpi_pcct_shared_memory tmp = {0};
+	u16 comm_space_size;
+	int ret;
+
+	/* Write signature for this subspace */
+	tmp.signature = HCCS_PCC_SIGNATURE_MASK | hdev->chan_id;
+	/* Write to the shared command region */
+	tmp.command = cmd;
+	/* Clear cmd complete bit */
+	tmp.status = 0;
+	memcpy_toio(comm_base, (void *)&tmp,
+			sizeof(struct acpi_pcct_shared_memory));
+
+	/* Copy the message to the PCC comm space */
+	comm_space_size = HCCS_PCC_SHARE_MEM_BYTES -
+				sizeof(struct acpi_pcct_shared_memory);
+	memcpy_toio(comm_space, (void *)desc, comm_space_size);
+
+	/* Ring doorbell */
+	ret = mbox_send_message(cl_info->mbox_chan, &cmd);
+	if (ret < 0) {
+		dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
+			ret);
+		goto end;
+	}
+
+	/* Wait for completion */
+	ret = hccs_check_chan_cmd_complete(hdev);
+	if (ret)
+		goto end;
+
+	/* Copy response data */
+	memcpy_fromio((void *)desc, comm_space, comm_space_size);
+	fw_inner_head = &desc->rsp.fw_inner_head;
+	if (fw_inner_head->retStatus) {
+		dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n",
+			fw_inner_head->retStatus);
+		ret = -EIO;
+	}
+
+end:
+	mbox_client_txdone(cl_info->mbox_chan, ret);
+	return ret;
+}
+
+static void hccs_init_req_desc(struct hccs_desc *desc)
+{
+	struct hccs_req_desc *req = &desc->req;
+
+	memset(desc, 0, sizeof(*desc));
+	req->req_head.module_code = HCCS_SERDES_MODULE_CODE;
+}
+
+static int hccs_get_dev_caps(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+	memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps));
+
+	return 0;
+}
+
+static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "query system chip number failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chip_num = *((u8 *)&desc.rsp.data);
+	if (!hdev->chip_num) {
+		dev_err(hdev->dev, "chip num obtained from firmware is zero.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_get_chip_info(struct hccs_dev *hdev,
+			      struct hccs_chip_info *chip)
+{
+	struct hccs_die_num_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_num_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc);
+	if (ret)
+		return ret;
+
+	chip->die_num = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	int ret;
+	u8 idx;
+
+	ret = hccs_query_chip_num_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	hdev->chips = devm_kzalloc(hdev->dev,
+				hdev->chip_num * sizeof(struct hccs_chip_info),
+				GFP_KERNEL);
+	if (!hdev->chips) {
+		dev_err(hdev->dev, "allocate all chips memory failed.\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < hdev->chip_num; idx++) {
+		chip = &hdev->chips[idx];
+		chip->chip_id = idx;
+		ret = hccs_get_chip_info(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n",
+				idx, ret);
+			return ret;
+		}
+		chip->hdev = hdev;
+	}
+
+	return 0;
+}
+
+static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id,
+				       u8 die_idx, struct hccs_die_info *die)
+{
+	struct hccs_die_info_req_param *req_param;
+	struct hccs_die_info_rsp_data *rsp_data;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_info_req_param *)desc.req.data;
+	req_param->chip_id = chip_id;
+	req_param->die_idx = die_idx;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc);
+	if (ret)
+		return ret;
+
+	rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data;
+	die->die_id = rsp_data->die_id;
+	die->port_num = rsp_data->port_num;
+	die->min_port_id = rsp_data->min_port_id;
+	die->max_port_id = rsp_data->max_port_id;
+	if (die->min_port_id > die->max_port_id) {
+		dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n",
+			die->min_port_id, die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+	if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) {
+		dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n",
+			die->max_port_id, die_idx);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		if (!chip->die_num)
+			continue;
+
+		chip->dies = devm_kzalloc(hdev->dev,
+				chip->die_num * sizeof(struct hccs_die_info),
+				GFP_KERNEL);
+		if (!chip->dies) {
+			dev_err(dev, "allocate all dies memory on chip%u failed.\n",
+				i);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			ret = hccs_query_die_info_on_chip(hdev, i, j, die);
+			if (ret) {
+				dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n",
+					j, i, ret);
+				return ret;
+			}
+			die->chip = chip;
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode,
+			    struct hccs_desc *desc,
+			    void *buf, size_t buf_len,
+			    struct hccs_rsp_head *rsp_head)
+{
+	struct hccs_rsp_head *head;
+	struct hccs_rsp_desc *rsp;
+	int ret;
+
+	ret = hccs_pcc_cmd_send(hdev, opcode, desc);
+	if (ret)
+		return ret;
+
+	rsp = &desc->rsp;
+	head = &rsp->rsp_head;
+	if (head->data_len > buf_len) {
+		dev_err(hdev->dev,
+			"buffer overflow (buf_len = %lu, data_len = %u)!\n",
+			buf_len, head->data_len);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, rsp->data, head->data_len);
+	*rsp_head = *head;
+
+	return 0;
+}
+
+static int hccs_get_all_port_attr(struct hccs_dev *hdev,
+				  struct hccs_die_info *die,
+				  struct hccs_port_attr *attrs, u16 size)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_req_head *req_head;
+	struct hccs_rsp_head rsp_head;
+	struct hccs_desc desc;
+	size_t left_buf_len;
+	u32 data_len = 0;
+	u8 start_id;
+	u8 *buf;
+	int ret;
+
+	buf = (u8 *)attrs;
+	left_buf_len = sizeof(struct hccs_port_attr) * size;
+	start_id = die->min_port_id;
+	while (start_id <= die->max_port_id) {
+		hccs_init_req_desc(&desc);
+		req_head = &desc.req.req_head;
+		req_head->start_id = start_id;
+		req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+		req_param->chip_id = die->chip->chip_id;
+		req_param->die_id = die->die_id;
+
+		ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc,
+				       buf + data_len, left_buf_len, &rsp_head);
+		if (ret) {
+			dev_err(hdev->dev,
+				"get the information of port%u on die%u failed, ret = %d.\n",
+				start_id, die->die_id, ret);
+			return ret;
+		}
+
+		data_len += rsp_head.data_len;
+		left_buf_len -= rsp_head.data_len;
+		if (unlikely(rsp_head.next_id <= start_id)) {
+			dev_err(hdev->dev,
+				"next port id (%u) is not greater than last start id (%u) on die%u.\n",
+				rsp_head.next_id, start_id, die->die_id);
+			return -EINVAL;
+		}
+		start_id = rsp_head.next_id;
+	}
+
+	return 0;
+}
+
+static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev,
+					 struct hccs_die_info *die)
+{
+	struct hccs_port_attr *attrs;
+	struct hccs_port_info *port;
+	int ret;
+	u8 i;
+
+	attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr),
+			GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		port->port_id = attrs[i].port_id;
+		port->port_type = attrs[i].port_type;
+		port->lane_mode = attrs[i].lane_mode;
+		port->enable = attrs[i].enable;
+		port->die = die;
+	}
+
+out:
+	kfree(attrs);
+	return ret;
+}
+
+static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev)
+{
+
+	struct device *dev = hdev->dev;
+	struct hccs_chip_info *chip;
+	struct hccs_die_info *die;
+	u8 i, j;
+	int ret;
+
+	for (i = 0; i < hdev->chip_num; i++) {
+		chip = &hdev->chips[i];
+		for (j = 0; j < chip->die_num; j++) {
+			die = &chip->dies[j];
+			if (!die->port_num)
+				continue;
+
+			die->ports = devm_kzalloc(dev,
+				die->port_num * sizeof(struct hccs_port_info),
+				GFP_KERNEL);
+			if (!die->ports) {
+				dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n",
+					i, die->die_id);
+				return -ENOMEM;
+			}
+
+			ret = hccs_get_all_port_info_on_die(hdev, die);
+			if (ret) {
+				dev_err(dev, "get all port info on chip%u/die%u failed, ret = %d.\n",
+					i, die->die_id, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int hccs_get_hw_info(struct hccs_dev *hdev)
+{
+	int ret;
+
+	ret = hccs_query_chip_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query chip info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_die_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	ret = hccs_query_all_port_info_on_platform(hdev);
+	if (ret) {
+		dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_probe(struct platform_device *pdev)
+{
+	struct acpi_device *acpi_dev;
+	struct hccs_dev *hdev;
+	int rc;
+
+	if (acpi_disabled) {
+		dev_err(&pdev->dev, "acpi is disabled.\n");
+		return -ENODEV;
+	}
+	acpi_dev = ACPI_COMPANION(&pdev->dev);
+	if (!acpi_dev)
+		return -ENODEV;
+
+	hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return -ENOMEM;
+	hdev->acpi_dev = acpi_dev;
+	hdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, hdev);
+
+	mutex_init(&hdev->lock);
+	rc = hccs_get_pcc_chan_id(hdev);
+	if (rc)
+		return rc;
+	rc = hccs_register_pcc_channel(hdev);
+	if (rc)
+		return rc;
+
+	rc = hccs_get_dev_caps(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	rc = hccs_get_hw_info(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
+	return 0;
+
+unregister_pcc_chan:
+	hccs_unregister_pcc_channel(hdev);
+
+	return rc;
+}
+
+static int hccs_remove(struct platform_device *pdev)
+{
+	struct hccs_dev *hdev = platform_get_drvdata(pdev);
+
+	hccs_unregister_pcc_channel(hdev);
+
+	return 0;
+}
+
+static const struct acpi_device_id hccs_acpi_match[] = {
+	{ "HISI04B1"},
+	{ ""},
+};
+MODULE_DEVICE_TABLE(acpi, hccs_acpi_match);
+
+static struct platform_driver hccs_driver = {
+	.probe = hccs_probe,
+	.remove = hccs_remove,
+	.driver = {
+		.name = "kunpeng_hccs",
+		.acpi_match_table = hccs_acpi_match,
+	},
+};
+
+module_platform_driver(hccs_driver);
+
+MODULE_DESCRIPTION("Kunpeng SoC HCCS driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>");
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
new file mode 100644
index 000000000000..fcc95e7e2a60
--- /dev/null
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2023 Hisilicon Limited. */
+
+#ifndef __KUNPENG_HCCS_H__
+#define __KUNPENG_HCCS_H__
+
+/*
+ * |---------------  Chip0  ---------------|----------------  ChipN  -------------|
+ * |--------Die0-------|--------DieN-------|--------Die0-------|-------DieN-------|
+ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 |
+ */
+
+/*
+ * This value cannot be 255, otherwise the loop of the multi-BD communication
+ * case cannot end.
+ */
+#define HCCS_DIE_MAX_PORT_ID	254
+
+struct hccs_port_info {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	bool enable; /* if the port is enabled */
+	struct hccs_die_info *die; /* point to the die the port is located */
+};
+
+struct hccs_die_info {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+	struct hccs_port_info *ports;
+	struct hccs_chip_info *chip; /* point to the chip the die is located */
+};
+
+struct hccs_chip_info {
+	u8 chip_id;
+	u8 die_num;
+	struct hccs_die_info *dies;
+	struct hccs_dev *hdev;
+};
+
+struct hccs_mbox_client_info {
+	struct mbox_client client;
+	struct mbox_chan *mbox_chan;
+	struct pcc_mbox_chan *pcc_chan;
+	u64 deadline_us;
+	void *pcc_comm_addr;
+};
+
+struct hccs_dev {
+	struct device *dev;
+	struct acpi_device *acpi_dev;
+	u64 caps;
+	u8 chip_num;
+	struct hccs_chip_info *chips;
+	u8 chan_id;
+	struct mutex lock;
+	struct hccs_mbox_client_info cl_info;
+};
+
+#define HCCS_SERDES_MODULE_CODE 0x32
+enum hccs_subcmd_type {
+	HCCS_GET_CHIP_NUM = 0x1,
+	HCCS_GET_DIE_NUM,
+	HCCS_GET_DIE_INFO,
+	HCCS_GET_DIE_PORT_INFO,
+	HCCS_GET_DEV_CAP,
+	HCCS_GET_PORT_LINK_STATUS,
+	HCCS_GET_PORT_CRC_ERR_CNT,
+	HCCS_GET_DIE_PORTS_LANE_STA,
+	HCCS_GET_DIE_PORTS_LINK_STA,
+	HCCS_GET_DIE_PORTS_CRC_ERR_CNT,
+	HCCS_SUB_CMD_MAX = 255,
+};
+
+struct hccs_die_num_req_param {
+	u8 chip_id;
+};
+
+struct hccs_die_info_req_param {
+	u8 chip_id;
+	u8 die_idx;
+};
+
+struct hccs_die_info_rsp_data {
+	u8 die_id;
+	u8 port_num;
+	u8 min_port_id;
+	u8 max_port_id;
+};
+
+struct hccs_port_attr {
+	u8 port_id;
+	u8 port_type;
+	u8 lane_mode;
+	u8 enable : 1; /* if the port is enabled */
+	u16 rsv[2];
+};
+
+/*
+ * The common command request for getting the information of all HCCS port on
+ * specified DIE.
+ */
+struct hccs_die_comm_req_param {
+	u8 chip_id;
+	u8 die_id; /* id in hardware */
+};
+
+struct hccs_req_head {
+	u8 module_code; /* set to 0x32 for serdes */
+	u8 start_id;
+	u8 rsv[2];
+};
+
+struct hccs_rsp_head {
+	u8 data_len;
+	u8 next_id;
+	u8 rsv[2];
+};
+
+struct hccs_fw_inner_head {
+	u8 retStatus; /* 0: success, other: failure */
+	u8 rsv[7];
+};
+
+#define HCCS_PCC_SHARE_MEM_BYTES	64
+#define HCCS_FW_INNER_HEAD_BYTES	8
+#define HCCS_RSP_HEAD_BYTES		4
+
+#define HCCS_MAX_RSP_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_FW_INNER_HEAD_BYTES - \
+					 HCCS_RSP_HEAD_BYTES)
+#define HCCS_MAX_RSP_DATA_SIZE_MAX	(HCCS_MAX_RSP_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the response data in the
+ * communication space based on the real length.
+ */
+struct hccs_rsp_desc {
+	struct hccs_fw_inner_head fw_inner_head; /* 8 Bytes */
+	struct hccs_rsp_head rsp_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_RSP_DATA_SIZE_MAX];
+};
+
+#define HCCS_REQ_HEAD_BYTES		4
+#define HCCS_MAX_REQ_DATA_BYTES		(HCCS_PCC_SHARE_MEM_BYTES - \
+					 HCCS_REQ_HEAD_BYTES)
+#define HCCS_MAX_REQ_DATA_SIZE_MAX	(HCCS_MAX_REQ_DATA_BYTES / 4)
+
+/*
+ * Note: Actual available size of data field also depands on the PCC header
+ * bytes of the specific type. Driver needs to copy the request data to the
+ * communication space based on the real length.
+ */
+struct hccs_req_desc {
+	struct hccs_req_head req_head; /* 4 Bytes */
+	u32 data[HCCS_MAX_REQ_DATA_SIZE_MAX];
+};
+
+struct hccs_desc {
+	union {
+		struct hccs_req_desc req;
+		struct hccs_rsp_desc rsp;
+	};
+};
+
+#endif /* __KUNPENG_HCCS_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v7 2/3] soc: hisilicon: add sysfs entry to query information of HCCS
  2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-08-08  2:36   ` [PATCH v7 1/3] " Huisong Li
@ 2023-08-08  2:36   ` Huisong Li
  2023-08-08  2:36   ` [PATCH v7 3/3] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
  2023-08-11  9:30   ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Wei Xu
  3 siblings, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-08-08  2:36 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap, zenghui.yu
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

This patch creates chip, die and port directory based on the actual
hardware implementation of platform. Some sysfs entries under these
directories are created to query the health status and port information
of HCCS.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/soc/hisilicon/kunpeng_hccs.c | 638 +++++++++++++++++++++++++++
 drivers/soc/hisilicon/kunpeng_hccs.h |  22 +
 2 files changed, 660 insertions(+)

diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c
index b61de32ce00c..6864d203c678 100644
--- a/drivers/soc/hisilicon/kunpeng_hccs.c
+++ b/drivers/soc/hisilicon/kunpeng_hccs.c
@@ -5,10 +5,27 @@
  *
  * Copyright (c) 2023 Hisilicon Limited.
  * Author: Huisong Li <lihuisong@huawei.com>
+ *
+ * HCCS driver for Kunpeng SoC provides the following features:
+ * - Retrieve the following information about each port:
+ *    - port type
+ *    - lane mode
+ *    - enable
+ *    - current lane mode
+ *    - link finite state machine
+ *    - lane mask
+ *    - CRC error count
+ *
+ * - Retrieve the following information about all the ports on the chip or
+ *   the die:
+ *    - if all enabled ports are in linked
+ *    - if all linked ports are in full lane
+ *    - CRC error count sum
  */
 #include <linux/acpi.h>
 #include <linux/iopoll.h>
 #include <linux/platform_device.h>
+#include <linux/sysfs.h>
 
 #include <acpi/pcc.h>
 
@@ -25,6 +42,21 @@
 #define HCCS_PCC_CMD_WAIT_RETRIES_NUM		500ULL
 #define HCCS_POLL_STATUS_TIME_INTERVAL_US	3
 
+static struct hccs_port_info *kobj_to_port_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_port_info, kobj);
+}
+
+static struct hccs_die_info *kobj_to_die_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_die_info, kobj);
+}
+
+static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k)
+{
+	return container_of(k, struct hccs_chip_info, kobj);
+}
+
 struct hccs_register_ctx {
 	struct device *dev;
 	u8 chan_id;
@@ -561,6 +593,607 @@ static int hccs_get_hw_info(struct hccs_dev *hdev)
 	return 0;
 }
 
+static int hccs_query_port_link_status(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       struct hccs_link_status *link_status)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port link status info failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	*link_status = *((struct hccs_link_status *)desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev,
+				       const struct hccs_port_info *port,
+				       u64 *crc_err_cnt)
+{
+	const struct hccs_die_info *die = port->die;
+	const struct hccs_chip_info *chip = die->chip;
+	struct hccs_port_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_port_comm_req_param *)desc.req.data;
+	req_param->chip_id = chip->chip_id;
+	req_param->die_id = die->die_id;
+	req_param->port_id = port->port_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get port crc error count failed, ret = %d.\n", ret);
+		return ret;
+	}
+
+	memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static int hccs_get_die_all_link_status(struct hccs_dev *hdev,
+					const struct hccs_die_info *die,
+					u8 *all_linked)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*all_linked = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LINK_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev,
+			"get link status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*all_linked = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev,
+					     const struct hccs_die_info *die,
+					     u8 *full_lane)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*full_lane = 1;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	*full_lane = *((u8 *)&desc.rsp.data);
+
+	return 0;
+}
+
+static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev,
+					  const struct hccs_die_info *die,
+					  u64 *total_crc_err_cnt)
+{
+	struct hccs_die_comm_req_param *req_param;
+	struct hccs_desc desc;
+	int ret;
+
+	if (die->port_num == 0) {
+		*total_crc_err_cnt = 0;
+		return 0;
+	}
+
+	hccs_init_req_desc(&desc);
+	req_param = (struct hccs_die_comm_req_param *)desc.req.data;
+	req_param->chip_id = die->chip->chip_id;
+	req_param->die_id = die->die_id;
+	ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc);
+	if (ret) {
+		dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n",
+			die->die_id, ret);
+		return ret;
+	}
+
+	memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64));
+
+	return 0;
+}
+
+static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+	struct kobj_attribute *kobj_attr;
+
+	kobj_attr = container_of(attr, struct kobj_attribute, attr);
+
+	return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops hccs_comm_ops = {
+	.show = hccs_show,
+};
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "HCCS-v%u\n", port->port_type);
+}
+static struct kobj_attribute hccs_type_attr = __ATTR_RO(type);
+
+static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "x%u\n", port->lane_mode);
+}
+static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode);
+
+static ssize_t enable_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+
+	return sysfs_emit(buf, "%u\n", port->enable);
+}
+static struct kobj_attribute port_enable_attr = __ATTR_RO(enable);
+
+static ssize_t cur_lane_num_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", link_status.lane_num);
+}
+static struct kobj_attribute cur_lane_num_attr = __ATTR_RO(cur_lane_num);
+
+static ssize_t link_fsm_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	const struct {
+		u8 link_fsm;
+		char *str;
+	} link_fsm_map[] = {
+		{HCCS_PORT_RESET, "reset"},
+		{HCCS_PORT_SETUP, "setup"},
+		{HCCS_PORT_CONFIG, "config"},
+		{HCCS_PORT_READY, "link-up"},
+	};
+	const char *link_fsm_str = "unknown";
+	size_t i;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) {
+		if (link_fsm_map[i].link_fsm == link_status.link_fsm) {
+			link_fsm_str = link_fsm_map[i].str;
+			break;
+		}
+	}
+
+	return sysfs_emit(buf, "%s\n", link_fsm_str);
+}
+static struct kobj_attribute link_fsm_attr = __ATTR_RO(link_fsm);
+
+static ssize_t lane_mask_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	struct hccs_link_status link_status = {0};
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_link_status(hdev, port, &link_status);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%x\n", link_status.lane_mask);
+}
+static struct kobj_attribute lane_mask_attr = __ATTR_RO(lane_mask);
+
+static ssize_t crc_err_cnt_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_port_info *port = kobj_to_port_info(kobj);
+	struct hccs_dev *hdev = port->die->chip->hdev;
+	u64 crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_attr = __ATTR_RO(crc_err_cnt);
+
+static struct attribute *hccs_port_default_attrs[] = {
+	&hccs_type_attr.attr,
+	&lane_mode_attr.attr,
+	&port_enable_attr.attr,
+	&cur_lane_num_attr.attr,
+	&link_fsm_attr.attr,
+	&lane_mask_attr.attr,
+	&crc_err_cnt_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_port_default);
+
+static const struct kobj_type hccs_port_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_port_default_groups,
+};
+
+static ssize_t all_linked_on_die_show(struct kobject *kobj,
+				      struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 all_linked;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_link_status(hdev, die, &all_linked);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_die_attr =
+		__ATTR(all_linked, 0444, all_linked_on_die_show, NULL);
+
+static ssize_t linked_full_lane_on_die_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u8 full_lane;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_die_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	const struct hccs_die_info *die = kobj_to_die_info(kobj);
+	struct hccs_dev *hdev = die->chip->hdev;
+	u64 total_crc_err_cnt;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt);
+	mutex_unlock(&hdev->lock);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_die_attr =
+	__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL);
+
+static struct attribute *hccs_die_default_attrs[] = {
+	&all_linked_on_die_attr.attr,
+	&linked_full_lane_on_die_attr.attr,
+	&crc_err_cnt_sum_on_die_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_die_default);
+
+static const struct kobj_type hccs_die_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_die_default_groups,
+};
+
+static ssize_t all_linked_on_chip_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 all_linked = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_link_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != all_linked) {
+			all_linked = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", all_linked);
+}
+static struct kobj_attribute all_linked_on_chip_attr =
+		__ATTR(all_linked, 0444, all_linked_on_chip_show, NULL);
+
+static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	u8 full_lane = 1;
+	u8 i, tmp;
+	int ret;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+		if (tmp != full_lane) {
+			full_lane = 0;
+			break;
+		}
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%u\n", full_lane);
+}
+static struct kobj_attribute linked_full_lane_on_chip_attr =
+	__ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL);
+
+static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	const struct hccs_chip_info *chip = kobj_to_chip_info(kobj);
+	u64 crc_err_cnt, total_crc_err_cnt = 0;
+	struct hccs_dev *hdev = chip->hdev;
+	const struct hccs_die_info *die;
+	int ret;
+	u16 i;
+
+	mutex_lock(&hdev->lock);
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt);
+		if (ret) {
+			mutex_unlock(&hdev->lock);
+			return ret;
+		}
+
+		total_crc_err_cnt += crc_err_cnt;
+	}
+	mutex_unlock(&hdev->lock);
+
+	return sysfs_emit(buf, "%llu\n", total_crc_err_cnt);
+}
+static struct kobj_attribute crc_err_cnt_sum_on_chip_attr =
+		__ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL);
+
+static struct attribute *hccs_chip_default_attrs[] = {
+	&all_linked_on_chip_attr.attr,
+	&linked_full_lane_on_chip_attr.attr,
+	&crc_err_cnt_sum_on_chip_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(hccs_chip_default);
+
+static const struct kobj_type hccs_chip_type = {
+	.sysfs_ops = &hccs_comm_ops,
+	.default_groups = hccs_chip_default_groups,
+};
+
+static void hccs_remove_die_dir(struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	u8 i;
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		if (port->dir_created)
+			kobject_put(&port->kobj);
+	}
+
+	kobject_put(&die->kobj);
+}
+
+static void hccs_remove_chip_dir(struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	u8 i;
+
+	for (i = 0; i < chip->die_num; i++) {
+		die = &chip->dies[i];
+		if (die->dir_created)
+			hccs_remove_die_dir(die);
+	}
+
+	kobject_put(&chip->kobj);
+}
+
+static void hccs_remove_topo_dirs(struct hccs_dev *hdev)
+{
+	u8 i;
+
+	for (i = 0; i < hdev->chip_num; i++)
+		hccs_remove_chip_dir(&hdev->chips[i]);
+}
+
+static int hccs_create_hccs_dir(struct hccs_dev *hdev,
+				struct hccs_die_info *die,
+				struct hccs_port_info *port)
+{
+	int ret;
+
+	ret = kobject_init_and_add(&port->kobj, &hccs_port_type,
+				   &die->kobj, "hccs%d", port->port_id);
+	if (ret) {
+		kobject_put(&port->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hccs_create_die_dir(struct hccs_dev *hdev,
+			       struct hccs_chip_info *chip,
+			       struct hccs_die_info *die)
+{
+	struct hccs_port_info *port;
+	int ret;
+	u16 i;
+
+	ret = kobject_init_and_add(&die->kobj, &hccs_die_type,
+				   &chip->kobj, "die%d", die->die_id);
+	if (ret) {
+		kobject_put(&die->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < die->port_num; i++) {
+		port = &die->ports[i];
+		ret = hccs_create_hccs_dir(hdev, die, port);
+		if (ret) {
+			dev_err(hdev->dev, "create hccs%d dir failed.\n",
+				port->port_id);
+			goto err;
+		}
+		port->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_die_dir(die);
+
+	return ret;
+}
+
+static int hccs_create_chip_dir(struct hccs_dev *hdev,
+				struct hccs_chip_info *chip)
+{
+	struct hccs_die_info *die;
+	int ret;
+	u16 id;
+
+	ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type,
+				   &hdev->dev->kobj, "chip%d", chip->chip_id);
+	if (ret) {
+		kobject_put(&chip->kobj);
+		return ret;
+	}
+
+	for (id = 0; id < chip->die_num; id++) {
+		die = &chip->dies[id];
+		ret = hccs_create_die_dir(hdev, chip, die);
+		if (ret)
+			goto err;
+		die->dir_created = true;
+	}
+
+	return 0;
+err:
+	hccs_remove_chip_dir(chip);
+
+	return ret;
+}
+
+static int hccs_create_topo_dirs(struct hccs_dev *hdev)
+{
+	struct hccs_chip_info *chip;
+	u8 id, k;
+	int ret;
+
+	for (id = 0; id < hdev->chip_num; id++) {
+		chip = &hdev->chips[id];
+		ret = hccs_create_chip_dir(hdev, chip);
+		if (ret) {
+			dev_err(hdev->dev, "init chip%d dir failed!\n", id);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (k = 0; k < id; k++)
+		hccs_remove_chip_dir(&hdev->chips[k]);
+
+	return ret;
+}
+
 static int hccs_probe(struct platform_device *pdev)
 {
 	struct acpi_device *acpi_dev;
@@ -598,6 +1231,10 @@ static int hccs_probe(struct platform_device *pdev)
 	if (rc)
 		goto unregister_pcc_chan;
 
+	rc = hccs_create_topo_dirs(hdev);
+	if (rc)
+		goto unregister_pcc_chan;
+
 	return 0;
 
 unregister_pcc_chan:
@@ -610,6 +1247,7 @@ static int hccs_remove(struct platform_device *pdev)
 {
 	struct hccs_dev *hdev = platform_get_drvdata(pdev);
 
+	hccs_remove_topo_dirs(hdev);
 	hccs_unregister_pcc_channel(hdev);
 
 	return 0;
diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h
index fcc95e7e2a60..9d71fb78443f 100644
--- a/drivers/soc/hisilicon/kunpeng_hccs.h
+++ b/drivers/soc/hisilicon/kunpeng_hccs.h
@@ -21,6 +21,8 @@ struct hccs_port_info {
 	u8 port_type;
 	u8 lane_mode;
 	bool enable; /* if the port is enabled */
+	struct kobject kobj;
+	bool dir_created;
 	struct hccs_die_info *die; /* point to the die the port is located */
 };
 
@@ -30,6 +32,8 @@ struct hccs_die_info {
 	u8 min_port_id;
 	u8 max_port_id;
 	struct hccs_port_info *ports;
+	struct kobject kobj;
+	bool dir_created;
 	struct hccs_chip_info *chip; /* point to the chip the die is located */
 };
 
@@ -37,6 +41,7 @@ struct hccs_chip_info {
 	u8 chip_id;
 	u8 die_num;
 	struct hccs_die_info *dies;
+	struct kobject kobj;
 	struct hccs_dev *hdev;
 };
 
@@ -107,6 +112,23 @@ struct hccs_die_comm_req_param {
 	u8 die_id; /* id in hardware */
 };
 
+/* The common command request for getting the information of a specific port */
+struct hccs_port_comm_req_param {
+	u8 chip_id;
+	u8 die_id;
+	u8 port_id;
+};
+
+#define HCCS_PORT_RESET         1
+#define HCCS_PORT_SETUP         2
+#define HCCS_PORT_CONFIG        3
+#define HCCS_PORT_READY         4
+struct hccs_link_status {
+	u8 lane_mask; /* indicate which lanes are used. */
+	u8 link_fsm : 3; /* link fsm, 1: reset 2: setup 3: config 4: link-up */
+	u8 lane_num : 5; /* current lane number */
+};
+
 struct hccs_req_head {
 	u8 module_code; /* set to 0x32 for serdes */
 	u8 start_id;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* [PATCH v7 3/3] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
  2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
  2023-08-08  2:36   ` [PATCH v7 1/3] " Huisong Li
  2023-08-08  2:36   ` [PATCH v7 2/3] soc: hisilicon: add sysfs entry to query information of HCCS Huisong Li
@ 2023-08-08  2:36   ` Huisong Li
  2023-08-11  9:30   ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Wei Xu
  3 siblings, 0 replies; 79+ messages in thread
From: Huisong Li @ 2023-08-08  2:36 UTC (permalink / raw)
  To: xuwei5, arnd, krzk, sudeep.holla, rdunlap, zenghui.yu
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong, lihuisong

Document the sysfs attributes description provided by HCCS driver on
Kunpeng SoC.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../sysfs-devices-platform-kunpeng_hccs       | 81 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 82 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs

diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
new file mode 100644
index 000000000000..fdb4e36310fb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
@@ -0,0 +1,81 @@
+What:		/sys/devices/platform/HISI04Bx:00/chipX/all_linked
+What:		/sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane
+What:		/sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified chip.
+		The X in 'chipX' indicates the Xth chip on platform.
+
+		There are following attributes in this directory:
+
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this chip are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this chip are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       chip.
+		================= ==== =========================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory
+		contains read-only attributes exposing some summarization
+		information of all HCCS ports under a specified die.
+		The Y in 'dieY' indicates the hardware id of the die on chip who
+		has chip id X.
+
+		There are following attributes in this directory:
+
+		================= ==== =========================================
+		all_linked:       (RO) if all enabled ports on this die are
+				       linked (bool).
+		linked_full_lane: (RO) if all linked ports on this die are full
+				       lane (bool).
+		crc_err_cnt:      (RO) total CRC err count for all ports on this
+				       die.
+		================= ==== =========================================
+
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/link_fsm
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask
+What:		/sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt
+Date:		November 2023
+KernelVersion:	6.6
+Contact:	Huisong Li <lihuisong@huawei.org>
+Description:
+		The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory
+		contains read-only attributes exposing information about
+		a HCCS port. The N value in 'hccsN' indicates this port id.
+		The X in 'chipX' indicates the ID of the chip to which the
+		HCCS port belongs. For example, X ranges from to 'n - 1' if the
+		chip number on platform is n.
+		The Y in 'dieY' indicates the hardware id of the die to which
+		the hccs port belongs.
+		Note: type, lane_mode and enable are fixed attributes on running
+		platform.
+
+		The HCCS port have the following attributes:
+
+		============= ==== =============================================
+		type:         (RO) port type (string), e.g. HCCS-v1 -> H32
+		lane_mode:    (RO) the lane mode of this port (string), e.g. x8
+		enable:       (RO) indicate if this port is enabled (bool).
+		cur_lane_num: (RO) current lane number of this port.
+		link_fsm:     (RO) link finite state machine of this port.
+		lane_mask:    (RO) current lane mask of this port, every bit
+			           indicates a lane.
+		crc_err_cnt:  (RO) CRC err count on this port.
+		============= ==== =============================================
diff --git a/MAINTAINERS b/MAINTAINERS
index 6d1ec10da20e..5a0ed6f283e7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9378,6 +9378,7 @@ F:	drivers/i2c/busses/i2c-hisi.c
 HISILICON KUNPENG SOC HCCS DRIVER
 M:	Huisong Li <lihuisong@huawei.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
 F:	drivers/soc/hisilicon/kunpeng_hccs.c
 F:	drivers/soc/hisilicon/kunpeng_hccs.h
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 79+ messages in thread

* Re: [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC
  2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
                     ` (2 preceding siblings ...)
  2023-08-08  2:36   ` [PATCH v7 3/3] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
@ 2023-08-11  9:30   ` Wei Xu
  3 siblings, 0 replies; 79+ messages in thread
From: Wei Xu @ 2023-08-11  9:30 UTC (permalink / raw)
  To: Huisong Li, arnd, krzk, sudeep.holla, rdunlap, zenghui.yu
  Cc: linux-kernel, soc, linux-arm-kernel, wanghuiqiang, tanxiaofei,
	liuyonglong

Hi Huisong,

On 2023/8/8 10:36, Huisong Li wrote:
> This series add HCCS driver to query the health status and port information
> of HCCS on Kunpeng SoC as well as document all sysfs entries provided by
> this driver.
> 
> ---
>  v7:
>  - split patch 1/2 to make it easier to review.                 
>  - fix a wrong command code in hccs_get_die_all_link_status(). 
>  - remove unused code and fix a log description in error branch.
> 
>  v6:
>  - fix the new entry in MAINTAINERS file to keep alphabetical order
> 
>  v5:
>  - fix document format to eliminate warning of making htmldocs.
> 
>  v4:
>  - remove useless header and reorder linux header.
>  - use __ATTR_RO to replace __ATTR for port attributes.
>  - add MODULE_DEVICE_TABLE to autoload the driver.
>  - update the date to "November 2023".
>  - fix some comments about HCCS description.
> 
>  v3:
>   - replace "using_status" with "enable" attribute.
>   - fix some comments in codes.
> 
>  v2:
>   - Document all sysfs entries provided by driver.
>   - drop 'pcc_type' and 'intr_mode' in struct hccs_dev.
>   - using _CRS with PCC GAS to get channel ID instead of _DSD.
>   - replace readw_relaxed_poll_timeout with readw_poll_timeout.
>   - use sysfs_emit() instead of sprintf().
>   - drop ACPI_PTR in hccs_driver.
>   - drop useless log during the probe phase.
> 
> Huisong Li (3):
>   soc: hisilicon: Support HCCS driver on Kunpeng SoC
>   soc: hisilicon: add sysfs entry to query information of HCCS
>   doc: soc: hisilicon: Add Kunpeng HCCS driver documentation
> 
>  .../sysfs-devices-platform-kunpeng_hccs       |   81 ++
>  MAINTAINERS                                   |    7 +
>  drivers/soc/Kconfig                           |    1 +
>  drivers/soc/Makefile                          |    1 +
>  drivers/soc/hisilicon/Kconfig                 |   20 +
>  drivers/soc/hisilicon/Makefile                |    2 +
>  drivers/soc/hisilicon/kunpeng_hccs.c          | 1275 +++++++++++++++++
>  drivers/soc/hisilicon/kunpeng_hccs.h          |  191 +++
>  8 files changed, 1578 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs
>  create mode 100644 drivers/soc/hisilicon/Kconfig
>  create mode 100644 drivers/soc/hisilicon/Makefile
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.c
>  create mode 100644 drivers/soc/hisilicon/kunpeng_hccs.h
> 

Thanks!
Series applied to the hisilicon driver tree.

Best Regards,
Wei

^ permalink raw reply	[flat|nested] 79+ messages in thread

end of thread, other threads:[~2023-08-11  9:31 UTC | newest]

Thread overview: 79+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-24  7:30 [PATCH] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
2023-04-24  8:09 ` Arnd Bergmann
2023-04-25  3:04   ` lihuisong (C)
2023-04-25  6:08     ` Arnd Bergmann
2023-04-25  9:42       ` lihuisong (C)
2023-04-25 10:30   ` Sudeep Holla
2023-04-25 13:00     ` lihuisong (C)
2023-04-25 13:19       ` Sudeep Holla
2023-04-26 12:12         ` lihuisong (C)
2023-05-04 13:16         ` lihuisong (C)
2023-05-15  3:37           ` lihuisong (C)
2023-05-15 13:08           ` Sudeep Holla
2023-05-16  7:35             ` lihuisong (C)
2023-05-16 12:29               ` Sudeep Holla
2023-05-16 14:13                 ` lihuisong (C)
2023-05-16 14:35                   ` Sudeep Holla
2023-05-17  7:16                     ` lihuisong (C)
2023-05-17  9:30                       ` Sudeep Holla
2023-05-17 11:35                         ` lihuisong (C)
2023-05-17 13:16                           ` Sudeep Holla
2023-05-18  8:24                             ` lihuisong (C)
2023-05-18  8:38                               ` Sudeep Holla
2023-05-18 12:29                                 ` lihuisong (C)
2023-04-24  8:42 ` Krzysztof Kozlowski
2023-04-25  3:16   ` lihuisong (C)
2023-04-25 11:24     ` Sudeep Holla
2023-05-22  7:22 ` [PATCH v2 0/2] " Huisong Li
2023-05-22  7:22   ` [PATCH v2 1/2] " Huisong Li
2023-05-23  9:39     ` Sudeep Holla
2023-05-23 11:57       ` lihuisong (C)
2023-05-23 13:41         ` Sudeep Holla
2023-05-24  9:36           ` lihuisong (C)
2023-05-25  2:41       ` lihuisong (C)
2023-05-25  7:35         ` Sudeep Holla
2023-05-25  8:12           ` lihuisong (C)
2023-05-30  2:53             ` lihuisong (C)
2023-05-30  8:43               ` Sudeep Holla
2023-05-30 10:57                 ` lihuisong (C)
2023-05-22  7:22   ` [PATCH v2 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-05-30 11:27 ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
2023-05-30 11:27   ` [PATCH v3 1/2] " Huisong Li
2023-05-30 11:27   ` [PATCH v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-06-19  6:32   ` [PATCH v3 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC lihuisong (C)
2023-07-14  6:17   ` lihuisong (C)
2023-07-17 12:06     ` Krzysztof Kozlowski
2023-07-18  8:07       ` lihuisong (C)
2023-07-18 10:59         ` Krzysztof Kozlowski
2023-07-18 14:00           ` lihuisong (C)
2023-07-18 11:01         ` Wei Xu
2023-07-20 12:43   ` lihuisong (C)
2023-07-25  7:57 ` [PATCH RESEND " Huisong Li
2023-07-25  7:57   ` [PATCH RESEND v3 1/2] " Huisong Li
2023-07-25  8:55     ` Wei Xu
2023-07-26  9:54       ` lihuisong (C)
2023-07-27  3:51         ` lihuisong (C)
2023-07-25 15:28     ` Randy Dunlap
2023-07-26  9:48       ` lihuisong (C)
2023-07-25  7:57   ` [PATCH RESEND v3 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-07-25  8:59     ` Wei Xu
2023-07-26  9:56       ` lihuisong (C)
2023-07-28  3:03 ` [PATCH v4 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
2023-07-28  3:03   ` [PATCH v4 1/2] " Huisong Li
2023-07-28  3:03   ` [PATCH v4 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-07-29  8:26 ` [PATCH v5 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
2023-07-29  8:26   ` [PATCH v5 1/2] " Huisong Li
2023-07-29 22:43     ` Randy Dunlap
2023-08-01  1:30       ` lihuisong (C)
2023-07-29  8:26   ` [PATCH v5 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-08-01  2:41 ` [PATCH v6 0/2] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
2023-08-01  2:41   ` [PATCH v6 1/2] " Huisong Li
2023-08-06 15:09     ` Zenghui Yu
2023-08-07  1:14       ` lihuisong (C)
2023-08-07  1:41       ` lihuisong (C)
2023-08-01  2:41   ` [PATCH v6 2/2] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-08-08  2:36 ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Huisong Li
2023-08-08  2:36   ` [PATCH v7 1/3] " Huisong Li
2023-08-08  2:36   ` [PATCH v7 2/3] soc: hisilicon: add sysfs entry to query information of HCCS Huisong Li
2023-08-08  2:36   ` [PATCH v7 3/3] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Huisong Li
2023-08-11  9:30   ` [PATCH v7 0/3] soc: hisilicon: Support HCCS driver on Kunpeng SoC Wei Xu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).