[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