[PATCH 2/7] soc: aspeed: Introduce core eSPI controller support

aspeedyh <[email protected]>
Newsgroups org.ozlabs.lists.openbmc,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.ozlabs.lists.linux-aspeed
Message-ID <[email protected]>
Add core eSPI controller support and common code for ASPEED SoCs. The
eSPI engine is a slave device in BMC to communicate with the Host over
the eSPI interface.

The initial support includes basic eSPI driver probe/remove operations,
and provides operators for ASPEED SoCs to implement their own eSPI slave
device drivers that are different among SoC models.

Signed-off-by: aspeedyh <[email protected]>
---
 drivers/soc/aspeed/Kconfig            |   7 ++
 drivers/soc/aspeed/Makefile           |   1 +
 drivers/soc/aspeed/espi/Makefile      |   1 +
 drivers/soc/aspeed/espi/aspeed-espi.c | 143 ++++++++++++++++++++++++++++++++++
 drivers/soc/aspeed/espi/aspeed-espi.h |  27 +++++++
 5 files changed, 179 insertions(+)

diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index f579ee0b5afa..677812fab11a 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -52,6 +52,13 @@ config ASPEED_SOCINFO
 	help
 	  Say yes to support decoding of ASPEED BMC information.
 
+config ASPEED_ESPI
+	tristate "ASPEED eSPI slave driver"
+	help
+	  Enable driver support for Aspeed eSPI controller. The eSPI controller
+	  plays as a slave device in BMC to communicate with the Host over the
+	  eSPI interface using peripheral, virtual wire, out of band, and flash
+	  channels.
 endmenu
 
 endif
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b35d74592964..79d794de428a 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)		+= aspeed-lpc-snoop.o
 obj-$(CONFIG_ASPEED_UART_ROUTING)	+= aspeed-uart-routing.o
 obj-$(CONFIG_ASPEED_P2A_CTRL)		+= aspeed-p2a-ctrl.o
 obj-$(CONFIG_ASPEED_SOCINFO)		+= aspeed-socinfo.o
+obj-$(CONFIG_ASPEED_ESPI)		+= espi/
diff --git a/drivers/soc/aspeed/espi/Makefile b/drivers/soc/aspeed/espi/Makefile
new file mode 100644
index 000000000000..d96dc030e23b
--- /dev/null
+++ b/drivers/soc/aspeed/espi/Makefile
@@ -0,0 +1 @@
+obj-y += aspeed-espi.o
diff --git a/drivers/soc/aspeed/espi/aspeed-espi.c b/drivers/soc/aspeed/espi/aspeed-espi.c
new file mode 100644
index 000000000000..15d58b38bbe4
--- /dev/null
+++ b/drivers/soc/aspeed/espi/aspeed-espi.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Unified Aspeed eSPI driver framework for different generation SoCs
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "aspeed-espi.h"
+
+struct aspeed_espi_ops {
+	void (*espi_pre_init)(struct aspeed_espi *espi);
+	void (*espi_post_init)(struct aspeed_espi *espi);
+	void (*espi_deinit)(struct aspeed_espi *espi);
+	irqreturn_t (*espi_isr)(int irq, void *espi);
+};
+
+static const struct of_device_id aspeed_espi_of_matches[] = {
+	{ }
+};
+MODULE_DEVICE_TABLE(of, aspeed_espi_of_matches);
+
+static int aspeed_espi_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct aspeed_espi *espi;
+	struct resource *res;
+	struct device *dev;
+	int rc;
+
+	dev = &pdev->dev;
+	espi = devm_kzalloc(dev, sizeof(*espi), GFP_KERNEL);
+	if (!espi)
+		return -ENOMEM;
+
+	espi->dev = dev;
+	match = of_match_device(aspeed_espi_of_matches, dev);
+	if (!match)
+		return -ENODEV;
+
+	espi->pdev = pdev;
+	espi->ops = match->data;
+	if (!espi->ops || !espi->ops->espi_isr)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "cannot get resource\n");
+		return -ENODEV;
+	}
+
+	espi->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(espi->regs)) {
+		dev_err(dev, "cannot map registers\n");
+		return PTR_ERR(espi->regs);
+	}
+
+	espi->irq = platform_get_irq(pdev, 0);
+	if (espi->irq < 0) {
+		dev_err(dev, "cannot get IRQ number\n");
+		return espi->irq;
+	}
+
+	espi->rst = devm_reset_control_get_optional(dev, NULL);
+	if (IS_ERR(espi->rst)) {
+		dev_err(dev, "cannot get reset control\n");
+		return PTR_ERR(espi->rst);
+	}
+
+	espi->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(espi->clk)) {
+		dev_err(dev, "cannot get clock control\n");
+		return PTR_ERR(espi->clk);
+	}
+
+	rc = clk_prepare_enable(espi->clk);
+	if (rc) {
+		dev_err(dev, "cannot enable clocks\n");
+		return rc;
+	}
+
+	if (espi->ops->espi_pre_init)
+		espi->ops->espi_pre_init(espi);
+
+	rc = devm_request_irq(dev, espi->irq, espi->ops->espi_isr, 0,
+			      dev_name(dev), espi);
+	if (rc) {
+		dev_err(dev, "cannot request IRQ\n");
+		goto err_deinit;
+	}
+
+	if (espi->ops->espi_post_init)
+		espi->ops->espi_post_init(espi);
+
+	platform_set_drvdata(pdev, espi);
+
+	dev_info(dev, "module loaded\n");
+
+	return 0;
+
+err_deinit:
+	if (espi->ops->espi_deinit)
+		espi->ops->espi_deinit(espi);
+	clk_disable_unprepare(espi->clk);
+
+	return rc;
+}
+
+static void aspeed_espi_remove(struct platform_device *pdev)
+{
+	struct aspeed_espi *espi;
+
+	espi = platform_get_drvdata(pdev);
+
+	if (!espi)
+		return;
+
+	if (espi->ops->espi_deinit)
+		espi->ops->espi_deinit(espi);
+
+	clk_disable_unprepare(espi->clk);
+}
+
+static struct platform_driver aspeed_espi_driver = {
+	.driver = {
+		.name = "aspeed-espi",
+		.of_match_table = aspeed_espi_of_matches,
+	},
+	.probe = aspeed_espi_probe,
+	.remove = aspeed_espi_remove,
+};
+
+module_platform_driver(aspeed_espi_driver);
+
+MODULE_AUTHOR("Aspeed Technology Inc.");
+MODULE_DESCRIPTION("Aspeed eSPI controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/aspeed/espi/aspeed-espi.h b/drivers/soc/aspeed/espi/aspeed-espi.h
new file mode 100644
index 000000000000..f4ad7f61fef6
--- /dev/null
+++ b/drivers/soc/aspeed/espi/aspeed-espi.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Unified eSPI driver header file and data structures
+ * Copyright 2026 Aspeed Technology Inc.
+ */
+#ifndef ASPEED_ESPI_H
+#define ASPEED_ESPI_H
+
+#include <linux/irqreturn.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define DEVICE_NAME		"aspeed-espi"
+
+struct aspeed_espi {
+	struct platform_device *pdev;
+	struct device *dev;
+	void __iomem *regs;
+	struct reset_control *rst;
+	struct clk *clk;
+	int dev_id;
+	int irq;
+	const struct aspeed_espi_ops *ops;
+};
+
+#endif

-- 
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.