lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <56bb279d-ad87-7dfb-ed1f-fede14f5a6f3@wanadoo.fr>
Date:   Wed, 18 May 2022 21:12:56 +0200
From:   Christophe JAILLET <christophe.jaillet@...adoo.fr>
To:     chiawei_wang@...eedtech.com
Cc:     a.kartashev@...ro.com, andrew@...id.au, devicetree@...r.kernel.org,
        dphadke@...ux.microsoft.com, jk@...econstruct.com.au,
        joel@....id.au, linux-arm-kernel@...ts.infradead.org,
        linux-aspeed@...ts.ozlabs.org, linux-kernel@...r.kernel.org,
        lkp@...el.com, openbmc@...ts.ozlabs.org,
        patrick.rudolph@...ements.com, robh+dt@...nel.org,
        ryan_chen@...eedtech.com
Subject: Re: [PATCH v5 3/4] soc: aspeed: Add eSPI driver

Hi,

inline a few comments about resources not freed in error handling paths 
in case it helps.

CJ

Le 16/05/2022 à 02:54, Chia-Wei Wang a écrit :
> The Aspeed eSPI controller is slave device to communicate with
> the master through the Enhanced Serial Peripheral Interface (eSPI).
> All of the four eSPI channels, namely peripheral, virtual wire,
> out-of-band, and flash are supported.
> 
> Signed-off-by: Chia-Wei Wang <chiawei_wang-SAlXDmAnmOAqDJ6do+/SaQ@...lic.gmane.org>
> Reported-by: kernel test robot <lkp-ral2JQCrhuEAvxtiuMwx3w@...lic.gmane.org>
> ---
>   drivers/soc/aspeed/Kconfig             |  11 +
>   drivers/soc/aspeed/Makefile            |   5 +
>   drivers/soc/aspeed/aspeed-espi-ctrl.c  | 214 ++++++++++
>   drivers/soc/aspeed/aspeed-espi-ctrl.h  | 309 ++++++++++++++
>   drivers/soc/aspeed/aspeed-espi-flash.c | 352 ++++++++++++++++
>   drivers/soc/aspeed/aspeed-espi-flash.h |  45 ++
>   drivers/soc/aspeed/aspeed-espi-ioc.h   | 195 +++++++++
>   drivers/soc/aspeed/aspeed-espi-oob.c   | 558 +++++++++++++++++++++++++
>   drivers/soc/aspeed/aspeed-espi-oob.h   |  70 ++++
>   drivers/soc/aspeed/aspeed-espi-perif.c | 511 ++++++++++++++++++++++
>   drivers/soc/aspeed/aspeed-espi-perif.h |  45 ++
>   drivers/soc/aspeed/aspeed-espi-vw.c    | 142 +++++++
>   drivers/soc/aspeed/aspeed-espi-vw.h    |  21 +
>   13 files changed, 2478 insertions(+)
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.h
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.h
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.c
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c
>   create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h
> 
> diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
> index f579ee0b5afa..b56414dc0743 100644
> --- a/drivers/soc/aspeed/Kconfig
> +++ b/drivers/soc/aspeed/Kconfig
> @@ -52,6 +52,17 @@ config ASPEED_SOCINFO
>   	help
>   	  Say yes to support decoding of ASPEED BMC information.
>   
> +config ASPEED_ESPI
> +	bool "ASPEED eSPI slave driver"
> +	select REGMAP
> +	select MFD_SYSCON
> +	default n
> +	help
> +	  Enable driver support for the Aspeed eSPI engine. The eSPI engine
> +	  plays as a slave device in BMC to communicate with the Host over
> +	  the eSPI interface. The four eSPI channels, namely peripheral,
> +	  virtual wire, out-of-band, and flash are supported.
> +
>   endmenu
>   
>   endif
> diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
> index b35d74592964..1bc433be7e93 100644
> --- a/drivers/soc/aspeed/Makefile
> +++ b/drivers/soc/aspeed/Makefile
> @@ -4,3 +4,8 @@ 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)		+= aspeed-espi-ctrl.o \
> +					   aspeed-espi-perif.o \
> +					   aspeed-espi-vw.o \
> +					   aspeed-espi-oob.o \
> +					   aspeed-espi-flash.o
> diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.c b/drivers/soc/aspeed/aspeed-espi-ctrl.c
> new file mode 100644
> index 000000000000..ce2967f851f2
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.c
> @@ -0,0 +1,214 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2021 Aspeed Technology Inc.
> + */
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> +
> +#include "aspeed-espi-ioc.h"
> +#include "aspeed-espi-ctrl.h"
> +#include "aspeed-espi-perif.h"
> +#include "aspeed-espi-vw.h"
> +#include "aspeed-espi-oob.h"
> +#include "aspeed-espi-flash.h"
> +
> +#define DEVICE_NAME "aspeed-espi-ctrl"
> +
> +static irqreturn_t aspeed_espi_ctrl_isr(int irq, void *arg)
> +{
> +	uint32_t sts;
> +	struct aspeed_espi_ctrl *espi_ctrl = (struct aspeed_espi_ctrl *)arg;
> +
> +	regmap_read(espi_ctrl->map, ESPI_INT_STS, &sts);
> +
> +	if (sts & ESPI_INT_STS_PERIF_BITS) {
> +		aspeed_espi_perif_event(sts, espi_ctrl->perif);
> +		regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_PERIF_BITS);
> +	}
> +
> +	if (sts & ESPI_INT_STS_VW_BITS) {
> +		aspeed_espi_vw_event(sts, espi_ctrl->vw);
> +		regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_VW_BITS);
> +	}
> +
> +	if (sts & (ESPI_INT_STS_OOB_BITS)) {
> +		aspeed_espi_oob_event(sts, espi_ctrl->oob);
> +		regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_OOB_BITS);
> +	}
> +
> +	if (sts & ESPI_INT_STS_FLASH_BITS) {
> +		aspeed_espi_flash_event(sts, espi_ctrl->flash);
> +		regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_FLASH_BITS);
> +	}
> +
> +	if (sts & ESPI_INT_STS_HW_RST_DEASSERT) {
> +		aspeed_espi_perif_enable(espi_ctrl->perif);
> +		aspeed_espi_vw_enable(espi_ctrl->vw);
> +		aspeed_espi_oob_enable(espi_ctrl->oob);
> +		aspeed_espi_flash_enable(espi_ctrl->flash);
> +
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0);
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0);
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff);
> +
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1);
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1);
> +
> +		if (espi_ctrl->model->version == ESPI_AST2500)
> +			regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T2,
> +				     ESPI_SYSEVT_INT_T2_HOST_RST_WARN |
> +				     ESPI_SYSEVT_INT_T2_OOB_RST_WARN);
> +
> +		regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_HW_RST_DEASSERT,
> +				   ESPI_INT_EN_HW_RST_DEASSERT);
> +
> +		regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT,
> +				   ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE,
> +				   ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE);
> +
> +		regmap_write(espi_ctrl->map, ESPI_INT_STS, ESPI_INT_STS_HW_RST_DEASSERT);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int aspeed_espi_ctrl_probe(struct platform_device *pdev)
> +{
> +	int rc = 0;
> +	struct aspeed_espi_ctrl *espi_ctrl;
> +	struct device *dev = &pdev->dev;
> +
> +	espi_ctrl = devm_kzalloc(dev, sizeof(*espi_ctrl), GFP_KERNEL);
> +	if (!espi_ctrl)
> +		return -ENOMEM;
> +
> +	espi_ctrl->model = of_device_get_match_data(dev);
> +
> +	espi_ctrl->map = syscon_node_to_regmap(dev->parent->of_node);
> +	if (IS_ERR(espi_ctrl->map)) {
> +		dev_err(dev, "cannot get remap\n");
> +		return -ENODEV;
> +	}
> +
> +	espi_ctrl->irq = platform_get_irq(pdev, 0);
> +	if (espi_ctrl->irq < 0)
> +		return espi_ctrl->irq;
> +
> +	espi_ctrl->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(espi_ctrl->clk)) {
> +		dev_err(dev, "cannot get clock\n");
> +		return -ENODEV;
> +	}
> +
> +	rc = clk_prepare_enable(espi_ctrl->clk);
> +	if (rc) {
> +		dev_err(dev, "cannot enable clock\n");
> +		return rc;
> +	}

This is never reverted, neither in an error handling path, nor in a 
.remove function. devm_add_action_or_reset()?

> +
> +	espi_ctrl->perif = aspeed_espi_perif_alloc(dev, espi_ctrl);
> +	if (IS_ERR(espi_ctrl->perif)) {
> +		dev_err(dev, "failed to allocate peripheral channel\n");
> +		return PTR_ERR(espi_ctrl->perif);
> +	}
> +
> +	espi_ctrl->vw = aspeed_espi_vw_alloc(dev, espi_ctrl);
> +	if (IS_ERR(espi_ctrl->vw)) {
> +		dev_err(dev, "failed to allocate virtual wire channel\n");
> +		return PTR_ERR(espi_ctrl->vw);
> +	}
> +
> +	espi_ctrl->oob = aspeed_espi_oob_alloc(dev, espi_ctrl);
> +	if (IS_ERR(espi_ctrl->oob)) {
> +		dev_err(dev, "failed to allocate out-of-band channel\n");
> +		return PTR_ERR(espi_ctrl->oob);
> +	}
> +
> +	espi_ctrl->flash = aspeed_espi_flash_alloc(dev, espi_ctrl);
> +	if (rc) {
> +		dev_err(dev, "failed to allocate flash channel\n");
> +		return PTR_ERR(espi_ctrl->flash);
> +	}
> +
> +	regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0);
> +	regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0);
> +	regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff);
> +
> +	regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1);
> +	regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1);
> +
> +	rc = devm_request_irq(dev, espi_ctrl->irq,
> +			      aspeed_espi_ctrl_isr,
> +			      0, DEVICE_NAME, espi_ctrl);
> +	if (rc) {
> +		dev_err(dev, "failed to request IRQ\n");
> +		return rc;
> +	}
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
> +			   ESPI_INT_EN_HW_RST_DEASSERT,
> +			   ESPI_INT_EN_HW_RST_DEASSERT);
> +
> +	dev_set_drvdata(dev, espi_ctrl);
> +
> +	dev_info(dev, "module loaded\n");
> +
> +	return 0;
> +}
> +
> +static int aspeed_espi_ctrl_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct aspeed_espi_ctrl *espi_ctrl = dev_get_drvdata(dev);
> +
> +	aspeed_espi_perif_free(dev, espi_ctrl->perif);
> +	aspeed_espi_vw_free(dev, espi_ctrl->vw);
> +	aspeed_espi_oob_free(dev, espi_ctrl->oob);
> +	aspeed_espi_flash_free(dev, espi_ctrl->flash);
> +
> +	return 0;
> +}
> +
> +static const struct aspeed_espi_model ast2500_model = {
> +	.version = ESPI_AST2500,
> +};
> +
> +static const struct aspeed_espi_model ast2600_model = {
> +	.version = ESPI_AST2600,
> +};
> +
> +static const struct of_device_id aspeed_espi_ctrl_of_matches[] = {
> +	{ .compatible = "aspeed,ast2500-espi-ctrl",
> +	  .data = &ast2500_model },
> +	{ .compatible = "aspeed,ast2600-espi-ctrl",
> +	  .data = &ast2600_model },
> +	{ },
> +};
> +
> +static struct platform_driver aspeed_espi_ctrl_driver = {
> +	.driver = {
> +		.name = DEVICE_NAME,
> +		.of_match_table = aspeed_espi_ctrl_of_matches,
> +	},
> +	.probe = aspeed_espi_ctrl_probe,
> +	.remove = aspeed_espi_ctrl_remove,
> +};
> +
> +module_platform_driver(aspeed_espi_ctrl_driver);
> +
> +MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang-SAlXDmAnmOAqDJ6do+/SaQ@...lic.gmane.org>");
> +MODULE_AUTHOR("Ryan Chen <ryan_chen-SAlXDmAnmOAqDJ6do+/SaQ@...lic.gmane.org>");
> +MODULE_DESCRIPTION("Control of Aspeed eSPI Slave Device");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.h b/drivers/soc/aspeed/aspeed-espi-ctrl.h
> new file mode 100644
> index 000000000000..8e26cd647a7f
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.h
> @@ -0,0 +1,309 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2021 Aspeed Technology Inc.
> + */
> +#ifndef _ASPEED_ESPI_CTRL_H_
> +#define _ASPEED_ESPI_CTRL_H_
> +
> +#include <linux/bits.h>
> +
> +enum aspeed_espi_version {
> +	ESPI_AST2500,
> +	ESPI_AST2600,
> +};
> +
> +struct aspeed_espi_model {
> +	uint32_t version;
> +};
> +
> +struct aspeed_espi_ctrl {
> +	struct device *dev;
> +
> +	struct regmap *map;
> +	struct clk *clk;
> +
> +	int irq;
> +
> +	struct aspeed_espi_perif *perif;
> +	struct aspeed_espi_vw *vw;
> +	struct aspeed_espi_oob *oob;
> +	struct aspeed_espi_flash *flash;
> +
> +	const struct aspeed_espi_model *model;
> +};
> +
> +/* eSPI register offset */
> +#define ESPI_CTRL		0x000
> +#define   ESPI_CTRL_OOB_RX_SW_RST		BIT(28)
> +#define   ESPI_CTRL_FLASH_TX_DMA_EN		BIT(23)
> +#define   ESPI_CTRL_FLASH_RX_DMA_EN		BIT(22)
> +#define   ESPI_CTRL_OOB_TX_DMA_EN		BIT(21)
> +#define   ESPI_CTRL_OOB_RX_DMA_EN		BIT(20)
> +#define   ESPI_CTRL_PERIF_NP_TX_DMA_EN		BIT(19)
> +#define   ESPI_CTRL_PERIF_PC_TX_DMA_EN		BIT(17)
> +#define   ESPI_CTRL_PERIF_PC_RX_DMA_EN		BIT(16)
> +#define   ESPI_CTRL_FLASH_SW_MODE_MASK		GENMASK(11, 10)
> +#define   ESPI_CTRL_FLASH_SW_MODE_SHIFT		10
> +#define   ESPI_CTRL_PERIF_PC_RX_DMA_EN		BIT(16)
> +#define   ESPI_CTRL_VW_GPIO_SW_MODE		BIT(9)
> +#define   ESPI_CTRL_FLASH_SW_RDY		BIT(7)
> +#define   ESPI_CTRL_OOB_SW_RDY			BIT(4)
> +#define   ESPI_CTRL_VW_SW_RDY			BIT(3)
> +#define   ESPI_CTRL_PERIF_SW_RDY		BIT(1)
> +#define ESPI_STS		0x004
> +#define ESPI_INT_STS		0x008
> +#define   ESPI_INT_STS_HW_RST_DEASSERT		BIT(31)
> +#define   ESPI_INT_STS_OOB_RX_TMOUT		BIT(23)
> +#define   ESPI_INT_STS_VW_SYSEVT1		BIT(22)
> +#define   ESPI_INT_STS_FLASH_TX_ERR		BIT(21)
> +#define   ESPI_INT_STS_OOB_TX_ERR		BIT(20)
> +#define   ESPI_INT_STS_FLASH_TX_ABT		BIT(19)
> +#define   ESPI_INT_STS_OOB_TX_ABT		BIT(18)
> +#define   ESPI_INT_STS_PERIF_NP_TX_ABT		BIT(17)
> +#define   ESPI_INT_STS_PERIF_PC_TX_ABT		BIT(16)
> +#define   ESPI_INT_STS_FLASH_RX_ABT		BIT(15)
> +#define   ESPI_INT_STS_OOB_RX_ABT		BIT(14)
> +#define   ESPI_INT_STS_PERIF_NP_RX_ABT		BIT(13)
> +#define   ESPI_INT_STS_PERIF_PC_RX_ABT		BIT(12)
> +#define   ESPI_INT_STS_PERIF_NP_TX_ERR		BIT(11)
> +#define   ESPI_INT_STS_PERIF_PC_TX_ERR		BIT(10)
> +#define   ESPI_INT_STS_VW_GPIOEVT		BIT(9)
> +#define   ESPI_INT_STS_VW_SYSEVT		BIT(8)
> +#define   ESPI_INT_STS_FLASH_TX_CMPLT		BIT(7)
> +#define   ESPI_INT_STS_FLASH_RX_CMPLT		BIT(6)
> +#define   ESPI_INT_STS_OOB_TX_CMPLT		BIT(5)
> +#define   ESPI_INT_STS_OOB_RX_CMPLT		BIT(4)
> +#define   ESPI_INT_STS_PERIF_NP_TX_CMPLT	BIT(3)
> +#define   ESPI_INT_STS_PERIF_PC_TX_CMPLT	BIT(1)
> +#define   ESPI_INT_STS_PERIF_PC_RX_CMPLT	BIT(0)
> +#define ESPI_INT_EN		0x00c
> +#define   ESPI_INT_EN_HW_RST_DEASSERT		BIT(31)
> +#define   ESPI_INT_EN_OOB_RX_TMOUT		BIT(23)
> +#define   ESPI_INT_EN_VW_SYSEVT1		BIT(22)
> +#define   ESPI_INT_EN_FLASH_TX_ERR		BIT(21)
> +#define   ESPI_INT_EN_OOB_TX_ERR		BIT(20)
> +#define   ESPI_INT_EN_FLASH_TX_ABT		BIT(19)
> +#define   ESPI_INT_EN_OOB_TX_ABT		BIT(18)
> +#define   ESPI_INT_EN_PERIF_NP_TX_ABT		BIT(17)
> +#define   ESPI_INT_EN_PERIF_PC_TX_ABT		BIT(16)
> +#define   ESPI_INT_EN_FLASH_RX_ABT		BIT(15)
> +#define   ESPI_INT_EN_OOB_RX_ABT		BIT(14)
> +#define   ESPI_INT_EN_PERIF_NP_RX_ABT		BIT(13)
> +#define   ESPI_INT_EN_PERIF_PC_RX_ABT		BIT(12)
> +#define   ESPI_INT_EN_PERIF_NP_TX_ERR		BIT(11)
> +#define   ESPI_INT_EN_PERIF_PC_TX_ERR		BIT(10)
> +#define   ESPI_INT_EN_VW_GPIOEVT		BIT(9)
> +#define   ESPI_INT_EN_VW_SYSEVT			BIT(8)
> +#define   ESPI_INT_EN_FLASH_TX_CMPLT		BIT(7)
> +#define   ESPI_INT_EN_FLASH_RX_CMPLT		BIT(6)
> +#define   ESPI_INT_EN_OOB_TX_CMPLT		BIT(5)
> +#define   ESPI_INT_EN_OOB_RX_CMPLT		BIT(4)
> +#define   ESPI_INT_EN_PERIF_NP_TX_CMPLT		BIT(3)
> +#define   ESPI_INT_EN_PERIF_PC_TX_CMPLT		BIT(1)
> +#define   ESPI_INT_EN_PERIF_PC_RX_CMPLT		BIT(0)
> +#define ESPI_PERIF_PC_RX_DMA	0x010
> +#define ESPI_PERIF_PC_RX_CTRL	0x014
> +#define   ESPI_PERIF_PC_RX_CTRL_PEND_SERV	BIT(31)
> +#define   ESPI_PERIF_PC_RX_CTRL_LEN_MASK	GENMASK(23, 12)
> +#define   ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT	12
> +#define   ESPI_PERIF_PC_RX_CTRL_TAG_MASK	GENMASK(11, 8)
> +#define   ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT	8
> +#define   ESPI_PERIF_PC_RX_CTRL_CYC_MASK	GENMASK(7, 0)
> +#define   ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT	0
> +#define ESPI_PERIF_PC_RX_PORT	0x018
> +#define ESPI_PERIF_PC_TX_DMA	0x020
> +#define ESPI_PERIF_PC_TX_CTRL	0x024
> +#define	  ESPI_PERIF_PC_TX_CTRL_TRIGGER		BIT(31)
> +#define	  ESPI_PERIF_PC_TX_CTRL_LEN_MASK	GENMASK(23, 12)
> +#define	  ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT	12
> +#define	  ESPI_PERIF_PC_TX_CTRL_TAG_MASK	GENMASK(11, 8)
> +#define	  ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT	8
> +#define	  ESPI_PERIF_PC_TX_CTRL_CYC_MASK	GENMASK(7, 0)
> +#define	  ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT	0
> +#define ESPI_PERIF_PC_TX_PORT	0x028
> +#define ESPI_PERIF_NP_TX_DMA	0x030
> +#define ESPI_PERIF_NP_TX_CTRL	0x034
> +#define   ESPI_PERIF_NP_TX_CTRL_TRIGGER		BIT(31)
> +#define	  ESPI_PERIF_NP_TX_CTRL_LEN_MASK	GENMASK(23, 12)
> +#define	  ESPI_PERIF_NP_TX_CTRL_LEN_SHIFT	12
> +#define	  ESPI_PERIF_NP_TX_CTRL_TAG_MASK	GENMASK(11, 8)
> +#define	  ESPI_PERIF_NP_TX_CTRL_TAG_SHIFT	8
> +#define	  ESPI_PERIF_NP_TX_CTRL_CYC_MASK	GENMASK(7, 0)
> +#define	  ESPI_PERIF_NP_TX_CTRL_CYC_SHIFT	0
> +#define ESPI_PERIF_NP_TX_PORT	0x038
> +#define ESPI_OOB_RX_DMA		0x040
> +#define ESPI_OOB_RX_CTRL	0x044
> +#define	  ESPI_OOB_RX_CTRL_PEND_SERV		BIT(31)
> +#define	  ESPI_OOB_RX_CTRL_LEN_MASK		GENMASK(23, 12)
> +#define	  ESPI_OOB_RX_CTRL_LEN_SHIFT		12
> +#define	  ESPI_OOB_RX_CTRL_TAG_MASK		GENMASK(11, 8)
> +#define	  ESPI_OOB_RX_CTRL_TAG_SHIFT		8
> +#define	  ESPI_OOB_RX_CTRL_CYC_MASK		GENMASK(7, 0)
> +#define	  ESPI_OOB_RX_CTRL_CYC_SHIFT		0
> +#define ESPI_OOB_RX_PORT	0x048
> +#define ESPI_OOB_TX_DMA		0x050
> +#define ESPI_OOB_TX_CTRL	0x054
> +#define	  ESPI_OOB_TX_CTRL_TRIGGER		BIT(31)
> +#define	  ESPI_OOB_TX_CTRL_LEN_MASK		GENMASK(23, 12)
> +#define	  ESPI_OOB_TX_CTRL_LEN_SHIFT		12
> +#define	  ESPI_OOB_TX_CTRL_TAG_MASK		GENMASK(11, 8)
> +#define	  ESPI_OOB_TX_CTRL_TAG_SHIFT		8
> +#define	  ESPI_OOB_TX_CTRL_CYC_MASK		GENMASK(7, 0)
> +#define	  ESPI_OOB_TX_CTRL_CYC_SHIFT		0
> +#define ESPI_OOB_TX_PORT	0x058
> +#define ESPI_FLASH_RX_DMA	0x060
> +#define ESPI_FLASH_RX_CTRL	0x064
> +#define	  ESPI_FLASH_RX_CTRL_PEND_SERV		BIT(31)
> +#define	  ESPI_FLASH_RX_CTRL_LEN_MASK		GENMASK(23, 12)
> +#define	  ESPI_FLASH_RX_CTRL_LEN_SHIFT		12
> +#define	  ESPI_FLASH_RX_CTRL_TAG_MASK		GENMASK(11, 8)
> +#define	  ESPI_FLASH_RX_CTRL_TAG_SHIFT		8
> +#define	  ESPI_FLASH_RX_CTRL_CYC_MASK		GENMASK(7, 0)
> +#define	  ESPI_FLASH_RX_CTRL_CYC_SHIFT		0
> +#define ESPI_FLASH_RX_PORT	0x068
> +#define ESPI_FLASH_TX_DMA	0x070
> +#define ESPI_FLASH_TX_CTRL	0x074
> +#define	  ESPI_FLASH_TX_CTRL_TRIGGER		BIT(31)
> +#define	  ESPI_FLASH_TX_CTRL_LEN_MASK		GENMASK(23, 12)
> +#define	  ESPI_FLASH_TX_CTRL_LEN_SHIFT		12
> +#define	  ESPI_FLASH_TX_CTRL_TAG_MASK		GENMASK(11, 8)
> +#define	  ESPI_FLASH_TX_CTRL_TAG_SHIFT		8
> +#define	  ESPI_FLASH_TX_CTRL_CYC_MASK		GENMASK(7, 0)
> +#define	  ESPI_FLASH_TX_CTRL_CYC_SHIFT		0
> +#define ESPI_FLASH_TX_PORT	0x078
> +#define ESPI_CTRL2		0x080
> +#define   ESPI_CTRL2_MEMCYC_RD_DIS		BIT(6)
> +#define   ESPI_CTRL2_MEMCYC_WR_DIS		BIT(4)
> +#define ESPI_PERIF_PC_RX_SADDR	0x084
> +#define ESPI_PERIF_PC_RX_TADDR	0x088
> +#define ESPI_PERIF_PC_RX_MASK	0x08c
> +#define   ESPI_PERIF_PC_RX_MASK_CFG_WP		BIT(0)
> +#define ESPI_SYSEVT_INT_EN	0x094
> +#define ESPI_SYSEVT		0x098
> +#define   ESPI_SYSEVT_HOST_RST_ACK		BIT(27)
> +#define   ESPI_SYSEVT_RST_CPU_INIT		BIT(26)
> +#define   ESPI_SYSEVT_SLV_BOOT_STS		BIT(23)
> +#define   ESPI_SYSEVT_NON_FATAL_ERR		BIT(22)
> +#define   ESPI_SYSEVT_FATAL_ERR			BIT(21)
> +#define   ESPI_SYSEVT_SLV_BOOT_DONE		BIT(20)
> +#define   ESPI_SYSEVT_OOB_RST_ACK		BIT(16)
> +#define   ESPI_SYSEVT_NMI_OUT			BIT(10)
> +#define   ESPI_SYSEVT_SMI_OUT			BIT(9)
> +#define   ESPI_SYSEVT_HOST_RST_WARN		BIT(8)
> +#define   ESPI_SYSEVT_OOB_RST_WARN		BIT(6)
> +#define   ESPI_SYSEVT_PLTRSTN			BIT(5)
> +#define   ESPI_SYSEVT_SUSPEND			BIT(4)
> +#define   ESPI_SYSEVT_S5_SLEEP			BIT(2)
> +#define   ESPI_SYSEVT_S4_SLEEP			BIT(1)
> +#define   ESPI_SYSEVT_S3_SLEEP			BIT(0)
> +#define ESPI_VW_GPIO_VAL	0x09c
> +#define ESPI_GEN_CAP_N_CONF	0x0a0
> +#define ESPI_CH0_CAP_N_CONF	0x0a4
> +#define ESPI_CH1_CAP_N_CONF	0x0a8
> +#define ESPI_CH2_CAP_N_CONF	0x0ac
> +#define ESPI_CH3_CAP_N_CONF	0x0b0
> +#define ESPI_CH3_CAP_N_CONF2	0x0b4
> +#define ESPI_SYSEVT1_INT_EN	0x100
> +#define ESPI_SYSEVT1		0x104
> +#define   ESPI_SYSEVT1_SUSPEND_ACK		BIT(20)
> +#define   ESPI_SYSEVT1_SUSPEND_WARN		BIT(0)
> +#define ESPI_SYSEVT_INT_T0	0x110
> +#define ESPI_SYSEVT_INT_T1	0x114
> +#define ESPI_SYSEVT_INT_T2	0x118
> +#define   ESPI_SYSEVT_INT_T2_HOST_RST_WARN	ESPI_SYSEVT_HOST_RST_WARN
> +#define   ESPI_SYSEVT_INT_T2_OOB_RST_WARN	ESPI_SYSEVT_OOB_RST_WARN
> +#define ESPI_SYSEVT_INT_STS	0x11c
> +#define   ESPI_SYSEVT_INT_STS_NMI_OUT		ESPI_SYSEVT_NMI_OUT
> +#define   ESPI_SYSEVT_INT_STS_SMI_OUT		ESPI_SYSEVT_SMI_OUT
> +#define   ESPI_SYSEVT_INT_STS_HOST_RST_WARN	ESPI_SYSEVT_HOST_RST_WARN
> +#define   ESPI_SYSEVT_INT_STS_OOB_RST_WARN	ESPI_SYSEVT_OOB_RST_WARN
> +#define   ESPI_SYSEVT_INT_STS_PLTRSTN		ESPI_SYSEVT_PLTRSTN
> +#define   ESPI_SYSEVT_INT_STS_SUSPEND		ESPI_SYSEVT_SUSPEND
> +#define   ESPI_SYSEVT_INT_STS_S5_SLEEP		ESPI_SYSEVT_INT_S5_SLEEP
> +#define   ESPI_SYSEVT_INT_STS_S4_SLEEP		ESPI_SYSEVT_INT_S4_SLEEP
> +#define   ESPI_SYSEVT_INT_STS_S3_SLEEP		ESPI_SYSEVT_INT_S3_SLEEP
> +#define ESPI_SYSEVT1_INT_T0	0x120
> +#define ESPI_SYSEVT1_INT_T1	0x124
> +#define ESPI_SYSEVT1_INT_T2	0x128
> +#define ESPI_SYSEVT1_INT_STS	0x12c
> +#define   ESPI_SYSEVT1_INT_STS_SUSPEND_WARN	ESPI_SYSEVT1_SUSPEND_WARN
> +#define ESPI_OOB_RX_DMA_RB_SIZE	0x130
> +#define ESPI_OOB_RX_DMA_RD_PTR	0x134
> +#define	  ESPI_OOB_RX_DMA_RD_PTR_UPDATE		BIT(31)
> +#define ESPI_OOB_RX_DMA_WS_PTR	0x138
> +#define   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN	BIT(31)
> +#define   ESPI_OOB_RX_DMA_WS_PTR_SP_MASK	GENMASK(27, 16)
> +#define   ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT	16
> +#define   ESPI_OOB_RX_DMA_WS_PTR_WP_MASK	GENMASK(11, 0)
> +#define   ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT	0
> +#define ESPI_OOB_TX_DMA_RB_SIZE	0x140
> +#define ESPI_OOB_TX_DMA_RD_PTR	0x144
> +#define	  ESPI_OOB_TX_DMA_RD_PTR_UPDATE		BIT(31)
> +#define ESPI_OOB_TX_DMA_WR_PTR	0x148
> +#define	  ESPI_OOB_TX_DMA_WR_PTR_SEND_EN	BIT(31)
> +
> +/* collect ESPI_INT_STS bits of eSPI channels for convenience */
> +#define ESPI_INT_STS_PERIF_BITS			\
> +	(ESPI_INT_STS_PERIF_NP_TX_ABT |		\
> +	 ESPI_INT_STS_PERIF_PC_TX_ABT |		\
> +	 ESPI_INT_STS_PERIF_NP_RX_ABT |		\
> +	 ESPI_INT_STS_PERIF_PC_RX_ABT |		\
> +	 ESPI_INT_STS_PERIF_NP_TX_ERR |		\
> +	 ESPI_INT_STS_PERIF_PC_TX_ERR |		\
> +	 ESPI_INT_STS_PERIF_NP_TX_CMPLT |	\
> +	 ESPI_INT_STS_PERIF_PC_TX_CMPLT |	\
> +	 ESPI_INT_STS_PERIF_PC_RX_CMPLT)
> +
> +#define ESPI_INT_STS_VW_BITS		\
> +	(ESPI_INT_STS_VW_SYSEVT1 |	\
> +	 ESPI_INT_STS_VW_GPIOEVT |	\
> +	 ESPI_INT_STS_VW_SYSEVT)
> +
> +#define ESPI_INT_STS_OOB_BITS		\
> +	(ESPI_INT_STS_OOB_RX_TMOUT |	\
> +	 ESPI_INT_STS_OOB_TX_ERR |	\
> +	 ESPI_INT_STS_OOB_TX_ABT |	\
> +	 ESPI_INT_STS_OOB_RX_ABT |	\
> +	 ESPI_INT_STS_OOB_TX_CMPLT |	\
> +	 ESPI_INT_STS_OOB_RX_CMPLT)
> +
> +#define ESPI_INT_STS_FLASH_BITS		\
> +	(ESPI_INT_STS_FLASH_TX_ERR |	\
> +	 ESPI_INT_STS_FLASH_TX_ABT |	\
> +	 ESPI_INT_STS_FLASH_RX_ABT |	\
> +	 ESPI_INT_STS_FLASH_TX_CMPLT |	\
> +	 ESPI_INT_STS_FLASH_RX_CMPLT)
> +
> +/* collect ESPI_INT_EN bits of eSPI channels for convenience */
> +#define ESPI_INT_EN_PERIF_BITS			\
> +	(ESPI_INT_EN_PERIF_NP_TX_ABT |		\
> +	 ESPI_INT_EN_PERIF_PC_TX_ABT |		\
> +	 ESPI_INT_EN_PERIF_NP_RX_ABT |		\
> +	 ESPI_INT_EN_PERIF_PC_RX_ABT |		\
> +	 ESPI_INT_EN_PERIF_NP_TX_ERR |		\
> +	 ESPI_INT_EN_PERIF_PC_TX_ERR |		\
> +	 ESPI_INT_EN_PERIF_NP_TX_CMPLT |	\
> +	 ESPI_INT_EN_PERIF_PC_TX_CMPLT |	\
> +	 ESPI_INT_EN_PERIF_PC_RX_CMPLT)
> +
> +#define ESPI_INT_EN_VW_BITS		\
> +	(ESPI_INT_EN_VW_SYSEVT1 |	\
> +	 ESPI_INT_EN_VW_GPIOEVT |	\
> +	 ESPI_INT_EN_VW_SYSEVT)
> +
> +#define ESPI_INT_EN_OOB_BITS		\
> +	(ESPI_INT_EN_OOB_RX_TMOUT |	\
> +	 ESPI_INT_EN_OOB_TX_ERR |	\
> +	 ESPI_INT_EN_OOB_TX_ABT |	\
> +	 ESPI_INT_EN_OOB_RX_ABT |	\
> +	 ESPI_INT_EN_OOB_TX_CMPLT |	\
> +	 ESPI_INT_EN_OOB_RX_CMPLT)
> +
> +#define ESPI_INT_EN_FLASH_BITS		\
> +	(ESPI_INT_EN_FLASH_TX_ERR |	\
> +	 ESPI_INT_EN_FLASH_TX_ABT |	\
> +	 ESPI_INT_EN_FLASH_RX_ABT |	\
> +	 ESPI_INT_EN_FLASH_TX_CMPLT |	\
> +	 ESPI_INT_EN_FLASH_RX_CMPLT)
> +
> +#endif
> diff --git a/drivers/soc/aspeed/aspeed-espi-flash.c b/drivers/soc/aspeed/aspeed-espi-flash.c
> new file mode 100644
> index 000000000000..d1ede22f22e3
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-flash.c
> @@ -0,0 +1,352 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2021 ASPEED Technology Inc.
> + */
> +#include <linux/fs.h>
> +#include <linux/of_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> +#include <linux/miscdevice.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "aspeed-espi-ioc.h"
> +#include "aspeed-espi-ctrl.h"
> +#include "aspeed-espi-flash.h"
> +
> +#define FLASH_MDEV_NAME	"aspeed-espi-flash"
> +
> +static long aspeed_espi_flash_get_rx(struct file *fp,
> +				     struct aspeed_espi_ioc *ioc,
> +				     struct aspeed_espi_flash *espi_flash)
> +{
> +	int i, rc = 0;
> +	unsigned long flags;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	uint32_t pkt_len;
> +	struct espi_comm_hdr *hdr;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl;
> +
> +	if (fp->f_flags & O_NONBLOCK) {
> +		if (mutex_trylock(&espi_flash->get_rx_mtx))
> +			return -EBUSY;
> +
> +		if (!espi_flash->rx_ready) {
> +			rc = -ENODATA;
> +			goto unlock_mtx_n_out;
> +		}
> +	} else {
> +		mutex_lock(&espi_flash->get_rx_mtx);
> +
> +		if (!espi_flash->rx_ready) {
> +			rc = wait_event_interruptible(espi_flash->wq,
> +						      espi_flash->rx_ready);
> +			if (rc == -ERESTARTSYS) {
> +				rc = -EINTR;
> +				goto unlock_mtx_n_out;
> +			}
> +		}
> +	}
> +
> +	/* common header (i.e. cycle type, tag, and length) is taken by HW */
> +	regmap_read(espi_ctrl->map, ESPI_FLASH_RX_CTRL, &reg);
> +	cyc = (reg & ESPI_FLASH_RX_CTRL_CYC_MASK) >> ESPI_FLASH_RX_CTRL_CYC_SHIFT;
> +	tag = (reg & ESPI_FLASH_RX_CTRL_TAG_MASK) >> ESPI_FLASH_RX_CTRL_TAG_SHIFT;
> +	len = (reg & ESPI_FLASH_RX_CTRL_LEN_MASK) >> ESPI_FLASH_RX_CTRL_LEN_SHIFT;
> +
> +	/*
> +	 * calculate the length of the rest part of the
> +	 * eSPI packet to be read from HW and copied to
> +	 * user space.
> +	 */
> +	switch (cyc) {
> +	case ESPI_FLASH_READ:
> +	case ESPI_FLASH_WRITE:
> +	case ESPI_FLASH_ERASE:
> +		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
> +			  sizeof(struct espi_flash_rwe);
> +		break;
> +	case ESPI_FLASH_SUC_CMPLT_D_MIDDLE:
> +	case ESPI_FLASH_SUC_CMPLT_D_FIRST:
> +	case ESPI_FLASH_SUC_CMPLT_D_LAST:
> +	case ESPI_FLASH_SUC_CMPLT_D_ONLY:
> +		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
> +			  sizeof(struct espi_flash_cmplt);
> +		break;
> +	case ESPI_FLASH_SUC_CMPLT:
> +	case ESPI_FLASH_UNSUC_CMPLT:
> +		pkt_len = len + sizeof(struct espi_flash_cmplt);
> +		break;
> +	default:
> +		rc = -EFAULT;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	if (ioc->pkt_len < pkt_len) {
> +		rc = -EINVAL;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	pkt = vmalloc(pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +	hdr->cyc = cyc;
> +	hdr->tag = tag;
> +	hdr->len_h = len >> 8;
> +	hdr->len_l = len & 0xff;
> +
> +	if (espi_flash->dma_mode) {
> +		memcpy(hdr + 1, espi_flash->dma.rx_virt,
> +		       pkt_len - sizeof(*hdr));
> +	} else {
> +		for (i = sizeof(*hdr); i < pkt_len; ++i) {
> +			regmap_read(espi_ctrl->map,
> +				    ESPI_FLASH_RX_PORT, &reg);
> +			pkt[i] = reg & 0xff;
> +		}
> +	}
> +
> +	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	spin_lock_irqsave(&espi_flash->lock, flags);
> +
> +	regmap_write_bits(espi_ctrl->map, ESPI_FLASH_RX_CTRL,
> +			  ESPI_FLASH_RX_CTRL_PEND_SERV,
> +			  ESPI_FLASH_RX_CTRL_PEND_SERV);
> +
> +	espi_flash->rx_ready = 0;
> +
> +	spin_unlock_irqrestore(&espi_flash->lock, flags);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_mtx_n_out:
> +	mutex_unlock(&espi_flash->get_rx_mtx);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_flash_put_tx(struct file *fp,
> +				     struct aspeed_espi_ioc *ioc,
> +				     struct aspeed_espi_flash *espi_flash)
> +{
> +	int i, rc = 0;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	struct espi_comm_hdr *hdr;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl;
> +
> +	if (!mutex_trylock(&espi_flash->put_tx_mtx))
> +		return -EAGAIN;
> +
> +	regmap_read(espi_ctrl->map, ESPI_FLASH_TX_CTRL, &reg);
> +	if (reg & ESPI_FLASH_TX_CTRL_TRIGGER) {
> +		rc = -EBUSY;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	pkt = vmalloc(ioc->pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +
> +	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	/*
> +	 * common header (i.e. cycle type, tag, and length)
> +	 * part is written to HW registers
> +	 */
> +	if (espi_flash->dma_mode) {
> +		memcpy(espi_flash->dma.tx_virt, hdr + 1,
> +		       ioc->pkt_len - sizeof(*hdr));
> +		dma_wmb();
> +	} else {
> +		for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
> +			regmap_write(espi_ctrl->map,
> +				     ESPI_FLASH_TX_PORT, pkt[i]);
> +	}
> +
> +	cyc = hdr->cyc;
> +	tag = hdr->tag;
> +	len = (hdr->len_h << 8) | (hdr->len_l & 0xff);
> +
> +	reg = ((cyc << ESPI_FLASH_TX_CTRL_CYC_SHIFT) & ESPI_FLASH_TX_CTRL_CYC_MASK)
> +		| ((tag << ESPI_FLASH_TX_CTRL_TAG_SHIFT) & ESPI_FLASH_TX_CTRL_TAG_MASK)
> +		| ((len << ESPI_FLASH_TX_CTRL_LEN_SHIFT) & ESPI_FLASH_TX_CTRL_LEN_MASK)
> +		| ESPI_FLASH_TX_CTRL_TRIGGER;
> +
> +	regmap_write(espi_ctrl->map, ESPI_FLASH_TX_CTRL, reg);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_mtx_n_out:
> +	mutex_unlock(&espi_flash->put_tx_mtx);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> +	struct aspeed_espi_ioc ioc;
> +	struct aspeed_espi_flash *espi_flash = container_of(
> +			fp->private_data,
> +			struct aspeed_espi_flash,
> +			mdev);
> +
> +	if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc)))
> +		return -EFAULT;
> +
> +	if (ioc.pkt_len > ESPI_PKT_LEN_MAX)
> +		return -EINVAL;
> +
> +	switch (cmd) {
> +	case ASPEED_ESPI_FLASH_GET_RX:
> +		return aspeed_espi_flash_get_rx(fp, &ioc, espi_flash);
> +	case ASPEED_ESPI_FLASH_PUT_TX:
> +		return aspeed_espi_flash_put_tx(fp, &ioc, espi_flash);
> +	};
> +
> +	return -EINVAL;
> +}
> +
> +void aspeed_espi_flash_event(uint32_t sts, struct aspeed_espi_flash *espi_flash)
> +{
> +	unsigned long flags;
> +
> +	if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) {
> +		spin_lock_irqsave(&espi_flash->lock, flags);
> +		espi_flash->rx_ready = 1;
> +		spin_unlock_irqrestore(&espi_flash->lock, flags);
> +		wake_up_interruptible(&espi_flash->wq);
> +	}
> +}
> +
> +void aspeed_espi_flash_enable(struct aspeed_espi_flash *espi_flash)
> +{
> +	struct aspeed_espi_flash_dma *dma = &espi_flash->dma;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl;
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_FLASH_SW_MODE_MASK,
> +			   (espi_flash->safs_mode << ESPI_CTRL_FLASH_SW_MODE_SHIFT));
> +
> +	if (espi_flash->dma_mode) {
> +		regmap_write(espi_ctrl->map, ESPI_FLASH_TX_DMA, dma->tx_addr);
> +		regmap_write(espi_ctrl->map, ESPI_FLASH_RX_DMA, dma->rx_addr);
> +		regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +				   ESPI_CTRL_FLASH_TX_DMA_EN | ESPI_CTRL_FLASH_RX_DMA_EN,
> +				   ESPI_CTRL_FLASH_TX_DMA_EN | ESPI_CTRL_FLASH_RX_DMA_EN);
> +	}
> +
> +	regmap_write(espi_ctrl->map, ESPI_INT_STS,
> +		     ESPI_INT_STS_FLASH_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
> +			   ESPI_INT_EN_FLASH_BITS,
> +			   ESPI_INT_EN_FLASH_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_FLASH_SW_RDY,
> +			   ESPI_CTRL_FLASH_SW_RDY);
> +}
> +
> +static const struct file_operations aspeed_espi_flash_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = aspeed_espi_flash_ioctl,
> +};
> +
> +void *aspeed_espi_flash_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl)
> +{
> +	int rc = 0;
> +	struct aspeed_espi_flash *espi_flash;
> +	struct aspeed_espi_flash_dma *dma;
> +
> +	espi_flash = devm_kzalloc(dev, sizeof(*espi_flash), GFP_KERNEL);
> +	if (!espi_flash)
> +		return ERR_PTR(-ENOMEM);
> +
> +	espi_flash->ctrl = espi_ctrl;
> +
> +	init_waitqueue_head(&espi_flash->wq);
> +
> +	spin_lock_init(&espi_flash->lock);
> +
> +	mutex_init(&espi_flash->put_tx_mtx);
> +	mutex_init(&espi_flash->get_rx_mtx);
> +
> +	if (of_property_read_bool(dev->of_node, "flash,dma-mode"))
> +		espi_flash->dma_mode = 1;
> +
> +	of_property_read_u32(dev->of_node, "flash,safs-mode", &espi_flash->safs_mode);
> +	if (espi_flash->safs_mode >= SAFS_MODES) {
> +		dev_err(dev, "invalid SAFS mode\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	if (espi_flash->dma_mode) {
> +		dma = &espi_flash->dma;
> +
> +		dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> +						  &dma->tx_addr, GFP_KERNEL);
> +		if (!dma->tx_virt) {
Here and a few lines below, dma_alloc_coherent() could be replaced by 
dmam_alloc_coherent() (its managed version) to simplify error handling 
and freeing of resources.

> +			dev_err(dev, "cannot allocate DMA TX buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +
> +		dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> +						  &dma->rx_addr, GFP_KERNEL);
> +		if (!dma->rx_virt) {
> +			dev_err(dev, "cannot allocate DMA RX buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +	}
> +
> +	espi_flash->mdev.parent = dev;
> +	espi_flash->mdev.minor = MISC_DYNAMIC_MINOR;
> +	espi_flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", FLASH_MDEV_NAME);
> +	espi_flash->mdev.fops = &aspeed_espi_flash_fops;
> +	rc = misc_register(&espi_flash->mdev);
> +	if (rc) {
> +		dev_err(dev, "cannot register device\n");
> +		return ERR_PTR(rc);
> +	}
> +
> +	aspeed_espi_flash_enable(espi_flash);
> +
> +	return espi_flash;
> +}
> +
> +void aspeed_espi_flash_free(struct device *dev, struct aspeed_espi_flash *espi_flash)
> +{
> +	struct aspeed_espi_flash_dma *dma = &espi_flash->dma;
> +
> +	if (espi_flash->dma_mode) {
> +		dma_free_coherent(dev, PAGE_SIZE, dma->tx_virt, dma->tx_addr);
> +		dma_free_coherent(dev, PAGE_SIZE, dma->rx_virt, dma->rx_addr);
> +	}
> +
> +	mutex_destroy(&espi_flash->put_tx_mtx);
> +	mutex_destroy(&espi_flash->get_rx_mtx);
> +
> +	misc_deregister(&espi_flash->mdev);
> +}
> diff --git a/drivers/soc/aspeed/aspeed-espi-flash.h b/drivers/soc/aspeed/aspeed-espi-flash.h
> new file mode 100644
> index 000000000000..bd5177329e50
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-flash.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2021 ASPEED Technology Inc.
> + */
> +#ifndef _ASPEED_ESPI_FLASH_H_
> +#define _ASPEED_ESPI_FLASH_H_
> +
> +enum aspeed_espi_flash_safs_mode {
> +	SAFS_MODE_MIX,
> +	SAFS_MODE_SW,
> +	SAFS_MODE_HW,
> +	SAFS_MODES,
> +};
> +
> +struct aspeed_espi_flash_dma {
> +	void *tx_virt;
> +	dma_addr_t tx_addr;
> +	void *rx_virt;
> +	dma_addr_t rx_addr;
> +};
> +
> +struct aspeed_espi_flash {
> +	uint32_t safs_mode;
> +
> +	uint32_t dma_mode;
> +	struct aspeed_espi_flash_dma dma;
> +
> +	uint32_t rx_ready;
> +	wait_queue_head_t wq;
> +
> +	struct mutex get_rx_mtx;
> +	struct mutex put_tx_mtx;
> +
> +	spinlock_t lock;
> +
> +	struct miscdevice mdev;
> +	struct aspeed_espi_ctrl *ctrl;
> +};
> +
> +void aspeed_espi_flash_event(uint32_t sts, struct aspeed_espi_flash *espi_flash);
> +void aspeed_espi_flash_enable(struct aspeed_espi_flash *espi_flash);
> +void *aspeed_espi_flash_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl);
> +void aspeed_espi_flash_free(struct device *dev, struct aspeed_espi_flash *espi_flash);
> +
> +#endif
> diff --git a/drivers/soc/aspeed/aspeed-espi-ioc.h b/drivers/soc/aspeed/aspeed-espi-ioc.h
> new file mode 100644
> index 000000000000..a78f1069841f
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-ioc.h
> @@ -0,0 +1,195 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2021 Aspeed Technology Inc.
> + */
> +#ifndef _ASPEED_ESPI_IOC_H
> +#define _ASPEED_ESPI_IOC_H
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +/*
> + * eSPI cycle type encoding
> + *
> + * Section 5.1 Cycle Types and Packet Format,
> + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016.
> + */
> +#define ESPI_PERIF_MEMRD32		0x00
> +#define ESPI_PERIF_MEMRD64		0x02
> +#define ESPI_PERIF_MEMWR32		0x01
> +#define ESPI_PERIF_MEMWR64		0x03
> +#define ESPI_PERIF_MSG			0x10
> +#define ESPI_PERIF_MSG_D		0x11
> +#define ESPI_PERIF_SUC_CMPLT		0x06
> +#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE	0x09
> +#define ESPI_PERIF_SUC_CMPLT_D_FIRST	0x0b
> +#define ESPI_PERIF_SUC_CMPLT_D_LAST	0x0d
> +#define ESPI_PERIF_SUC_CMPLT_D_ONLY	0x0f
> +#define ESPI_PERIF_UNSUC_CMPLT		0x0c
> +#define ESPI_OOB_MSG			0x21
> +#define ESPI_FLASH_READ			0x00
> +#define ESPI_FLASH_WRITE		0x01
> +#define ESPI_FLASH_ERASE		0x02
> +#define ESPI_FLASH_SUC_CMPLT		0x06
> +#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE	0x09
> +#define ESPI_FLASH_SUC_CMPLT_D_FIRST	0x0b
> +#define ESPI_FLASH_SUC_CMPLT_D_LAST	0x0d
> +#define ESPI_FLASH_SUC_CMPLT_D_ONLY	0x0f
> +#define ESPI_FLASH_UNSUC_CMPLT		0x0c
> +
> +/*
> + * eSPI packet format structure
> + *
> + * Section 5.1 Cycle Types and Packet Format,
> + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016.
> + */
> +struct espi_comm_hdr {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +};
> +
> +struct espi_perif_mem32 {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint32_t addr_be;
> +	uint8_t data[];
> +} __packed;
> +
> +struct espi_perif_mem64 {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint32_t addr_be;
> +	uint8_t data[];
> +} __packed;
> +
> +struct espi_perif_msg {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint8_t msg_code;
> +	uint8_t msg_byte[4];
> +	uint8_t data[];
> +} __packed;
> +
> +struct espi_perif_cmplt {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint8_t data[];
> +} __packed;
> +
> +struct espi_oob_msg {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint8_t data[];
> +};
> +
> +struct espi_flash_rwe {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint32_t addr_be;
> +	uint8_t data[];
> +} __packed;
> +
> +struct espi_flash_cmplt {
> +	uint8_t cyc;
> +	uint8_t len_h : 4;
> +	uint8_t tag : 4;
> +	uint8_t len_l;
> +	uint8_t data[];
> +} __packed;
> +
> +struct aspeed_espi_ioc {
> +	uint32_t pkt_len;
> +	uint8_t *pkt;
> +};
> +
> +/*
> + * we choose the longest header and the max payload size
> + * based on the Intel specification to define the maximum
> + * eSPI packet length
> + */
> +#define ESPI_PLD_LEN_MIN	(1UL << 6)
> +#define ESPI_PLD_LEN_MAX	(1UL << 12)
> +#define ESPI_PKT_LEN_MAX	(sizeof(struct espi_perif_msg) + ESPI_PLD_LEN_MAX)
> +
> +#define __ASPEED_ESPI_IOCTL_MAGIC	0xb8
> +
> +/*
> + * The IOCTL-based interface works in the eSPI packet in/out paradigm.
> + *
> + * Only the virtual wire IOCTL is a special case which does not send
> + * or receive an eSPI packet. However, to keep a more consisten use from
> + * userspace, we make all of the four channel drivers serve through the
> + * IOCTL interface.
> + *
> + * For the eSPI packet format, refer to
> + *   Section 5.1 Cycle Types and Packet Format,
> + *   Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016.
> + *
> + * For the example user apps using these IOCTL, refer to
> + *   https://github.com/AspeedTech-BMC/aspeed_app/tree/master/espi_test
> + */
> +
> +/*
> + * Peripheral Channel (CH0)
> + *  - ASPEED_ESPI_PERIF_PC_GET_RX
> + *      Receive an eSPI Posted/Completion packet
> + *  - ASPEED_ESPI_PERIF_PC_PUT_TX
> + *      Transmit an eSPI Posted/Completion packet
> + *  - ASPEED_ESPI_PERIF_NP_PUT_TX
> + *      Transmit an eSPI Non-Posted packet
> + */
> +#define ASPEED_ESPI_PERIF_PC_GET_RX	_IOR(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x00, struct aspeed_espi_ioc)
> +#define ASPEED_ESPI_PERIF_PC_PUT_TX	_IOW(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x01, struct aspeed_espi_ioc)
> +#define ASPEED_ESPI_PERIF_NP_PUT_TX	_IOW(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x02, struct aspeed_espi_ioc)
> +/*
> + * Virtual Wire Channel (CH1)
> + *  - ASPEED_ESPI_VW_GET_GPIO_VAL
> + *      Read the input value of GPIO over the VW channel
> + *  - ASPEED_ESPI_VW_PUT_GPIO_VAL
> + *      Write the output value of GPIO over the VW channel
> + */
> +#define ASPEED_ESPI_VW_GET_GPIO_VAL	_IOR(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x10, uint8_t)
> +#define ASPEED_ESPI_VW_PUT_GPIO_VAL	_IOW(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x11, uint8_t)
> +/*
> + * Out-of-band Channel (CH2)
> + *  - ASPEED_ESPI_OOB_GET_RX
> + *      Receive an eSPI OOB packet
> + *  - ASPEED_ESPI_OOB_PUT_TX
> + *      Transmit an eSPI OOB packet
> + */
> +#define ASPEED_ESPI_OOB_GET_RX		_IOR(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x20, struct aspeed_espi_ioc)
> +#define ASPEED_ESPI_OOB_PUT_TX		_IOW(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x21, struct aspeed_espi_ioc)
> +/*
> + * Flash Channel (CH3)
> + *  - ASPEED_ESPI_FLASH_GET_RX
> + *      Receive an eSPI flash packet
> + *  - ASPEED_ESPI_FLASH_PUT_TX
> + *      Transmit an eSPI flash packet
> + */
> +#define ASPEED_ESPI_FLASH_GET_RX	_IOR(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x30, struct aspeed_espi_ioc)
> +#define ASPEED_ESPI_FLASH_PUT_TX	_IOW(__ASPEED_ESPI_IOCTL_MAGIC, \
> +					     0x31, struct aspeed_espi_ioc)
> +
> +#endif
> diff --git a/drivers/soc/aspeed/aspeed-espi-oob.c b/drivers/soc/aspeed/aspeed-espi-oob.c
> new file mode 100644
> index 000000000000..2e0cc427b6c1
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-oob.c
> @@ -0,0 +1,558 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2021 Aspeed Technology Inc.
> + */
> +#include <linux/fs.h>
> +#include <linux/of_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> +#include <linux/miscdevice.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "aspeed-espi-ioc.h"
> +#include "aspeed-espi-ctrl.h"
> +#include "aspeed-espi-oob.h"
> +
> +#define OOB_MDEV_NAME	"aspeed-espi-oob"
> +
> +/* DMA descriptor is supported since AST2600 */
> +#define OOB_DMA_DESC_MAX_NUM	1024
> +#define OOB_DMA_TX_DESC_CUST	0x04
> +
> +/* descriptor-based RX DMA handling */
> +static long aspeed_espi_oob_dma_desc_get_rx(struct file *fp,
> +					    struct aspeed_espi_ioc *ioc,
> +					    struct aspeed_espi_oob *espi_oob)
> +{
> +	int rc = 0;
> +	unsigned long flags;
> +	uint32_t reg;
> +	uint32_t wptr, sptr;
> +	uint8_t *pkt;
> +	uint32_t pkt_len;
> +	struct espi_comm_hdr *hdr;
> +	struct oob_rx_dma_desc *d;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl;
> +
> +	regmap_read(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, &reg);
> +	wptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT;
> +	sptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT;
> +
> +	d = &espi_oob->dma.rx_desc[sptr];
> +
> +	if (!d->dirty)
> +		return -EFAULT;
> +
> +	pkt_len = ((d->len) ? d->len : 0x1000) + sizeof(struct espi_comm_hdr);
> +
> +	if (ioc->pkt_len < pkt_len)
> +		return -EINVAL;
> +
> +	pkt = vmalloc(pkt_len);
> +	if (!pkt)
> +		return -ENOMEM;
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +	hdr->cyc = d->cyc;
> +	hdr->tag = d->tag;
> +	hdr->len_h = d->len >> 8;
> +	hdr->len_l = d->len & 0xff;
> +	memcpy(hdr + 1, espi_oob->dma.rx_virt + (PAGE_SIZE * sptr), pkt_len - sizeof(*hdr));
> +
> +	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	spin_lock_irqsave(&espi_oob->lock, flags);
> +
> +	/* make current descriptor available again */
> +	d->dirty = 0;
> +
> +	sptr = (sptr + 1) % espi_oob->dma.rx_desc_num;
> +	wptr = (wptr + 1) % espi_oob->dma.rx_desc_num;
> +
> +	reg = ((wptr << ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT) & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK)
> +		| ((sptr << ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT) & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK)
> +		| ESPI_OOB_RX_DMA_WS_PTR_RECV_EN;
> +	regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, reg);
> +
> +	/* set ready flag base on the next RX descriptor */
> +	espi_oob->rx_ready = espi_oob->dma.rx_desc[sptr].dirty;
> +
> +	spin_unlock_irqrestore(&espi_oob->lock, flags);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_oob_get_rx(struct file *fp,
> +				   struct aspeed_espi_ioc *ioc,
> +				   struct aspeed_espi_oob *espi_oob)
> +{
> +	int i, rc = 0;
> +	unsigned long flags;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	uint32_t pkt_len;
> +	struct espi_comm_hdr *hdr;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl;
> +
> +	if (fp->f_flags & O_NONBLOCK) {
> +		if (mutex_trylock(&espi_oob->get_rx_mtx))
> +			return -EBUSY;
> +
> +		if (!espi_oob->rx_ready) {
> +			rc = -ENODATA;
> +			goto unlock_mtx_n_out;
> +		}
> +	} else {
> +		mutex_lock(&espi_oob->get_rx_mtx);
> +
> +		if (!espi_oob->rx_ready) {
> +			rc = wait_event_interruptible(espi_oob->wq,
> +						      espi_oob->rx_ready);
> +			if (rc == -ERESTARTSYS) {
> +				rc = -EINTR;
> +				goto unlock_mtx_n_out;
> +			}
> +		}
> +	}
> +
> +	if (espi_oob->dma_mode && espi_ctrl->model->version != ESPI_AST2500) {
> +		rc = aspeed_espi_oob_dma_desc_get_rx(fp, ioc, espi_oob);
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	/* common header (i.e. cycle type, tag, and length) is taken by HW */
> +	regmap_read(espi_ctrl->map, ESPI_OOB_RX_CTRL, &reg);
> +	cyc = (reg & ESPI_OOB_RX_CTRL_CYC_MASK) >> ESPI_OOB_RX_CTRL_CYC_SHIFT;
> +	tag = (reg & ESPI_OOB_RX_CTRL_TAG_MASK) >> ESPI_OOB_RX_CTRL_TAG_SHIFT;
> +	len = (reg & ESPI_OOB_RX_CTRL_LEN_MASK) >> ESPI_OOB_RX_CTRL_LEN_SHIFT;
> +
> +	/*
> +	 * calculate the length of the rest part of the
> +	 * eSPI packet to be read from HW and copied to
> +	 * user space.
> +	 */
> +	pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + sizeof(struct espi_comm_hdr);
> +
> +	if (ioc->pkt_len < pkt_len) {
> +		rc = -EINVAL;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	pkt = vmalloc(pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +	hdr->cyc = cyc;
> +	hdr->tag = tag;
> +	hdr->len_h = len >> 8;
> +	hdr->len_l = len & 0xff;
> +
> +	if (espi_oob->dma_mode) {
> +		memcpy(hdr + 1, espi_oob->dma.rx_virt,
> +		       pkt_len - sizeof(*hdr));
> +	} else {
> +		for (i = sizeof(*hdr); i < pkt_len; ++i) {
> +			regmap_read(espi_ctrl->map,
> +				    ESPI_OOB_RX_PORT, &reg);
> +			pkt[i] = reg & 0xff;
> +		}
> +	}
> +
> +	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	spin_lock_irqsave(&espi_oob->lock, flags);
> +
> +	regmap_write_bits(espi_ctrl->map, ESPI_OOB_RX_CTRL,
> +			  ESPI_OOB_RX_CTRL_PEND_SERV,
> +			  ESPI_OOB_RX_CTRL_PEND_SERV);
> +
> +	espi_oob->rx_ready = 0;
> +
> +	spin_unlock_irqrestore(&espi_oob->lock, flags);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_mtx_n_out:
> +	mutex_unlock(&espi_oob->get_rx_mtx);
> +
> +	return rc;
> +}
> +
> +/* descriptor-based TX DMA handling */
> +static long aspeed_espi_oob_dma_desc_put_tx(struct file *fp,
> +					    struct aspeed_espi_ioc *ioc,
> +					    struct aspeed_espi_oob *espi_oob)
> +{
> +	int rc = 0;
> +	uint32_t rptr, wptr;
> +	uint8_t *pkt;
> +	struct espi_comm_hdr *hdr;
> +	struct oob_tx_dma_desc *d;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl;
> +
> +	pkt = vzalloc(ioc->pkt_len);
> +	if (!pkt)
> +		return -ENOMEM;
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +
> +	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	/* kick HW to reflect the up-to-date read/write pointer */
> +	regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_RD_PTR,
> +		     ESPI_OOB_TX_DMA_RD_PTR_UPDATE);
> +
> +	regmap_read(espi_ctrl->map, ESPI_OOB_TX_DMA_RD_PTR, &rptr);
> +	regmap_read(espi_ctrl->map, ESPI_OOB_TX_DMA_WR_PTR, &wptr);
> +
> +	if (((wptr + 1) % espi_oob->dma.tx_desc_num) == rptr) {
> +		rc = -EBUSY;
> +		goto free_n_out;
> +	}
> +
> +	d = &espi_oob->dma.tx_desc[wptr];
> +	d->cyc = hdr->cyc;
> +	d->tag = hdr->tag;
> +	d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff);
> +	d->msg_type = OOB_DMA_TX_DESC_CUST;
> +
> +	memcpy(espi_oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1,
> +	       ioc->pkt_len - sizeof(*hdr));
> +
> +	dma_wmb();
> +
> +	wptr = (wptr + 1) % espi_oob->dma.tx_desc_num;
> +	wptr |= ESPI_OOB_TX_DMA_WR_PTR_SEND_EN;
> +	regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_WR_PTR, wptr);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_oob_put_tx(struct file *fp,
> +				   struct aspeed_espi_ioc *ioc,
> +				   struct aspeed_espi_oob *espi_oob)
> +{
> +	int i, rc = 0;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	struct espi_comm_hdr *hdr;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl;
> +
> +	if (!mutex_trylock(&espi_oob->put_tx_mtx))
> +		return -EBUSY;
> +
> +	if (espi_oob->dma_mode && espi_ctrl->model->version != ESPI_AST2500) {
> +		rc = aspeed_espi_oob_dma_desc_put_tx(fp, ioc, espi_oob);
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	regmap_read(espi_ctrl->map, ESPI_OOB_TX_CTRL, &reg);
> +	if (reg & ESPI_OOB_TX_CTRL_TRIGGER) {
> +		rc = -EBUSY;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	if (ioc->pkt_len > ESPI_PKT_LEN_MAX) {
> +		rc = -EINVAL;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	pkt = vmalloc(ioc->pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +
> +	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	/*
> +	 * common header (i.e. cycle type, tag, and length)
> +	 * part is written to HW registers
> +	 */
> +	if (espi_oob->dma_mode) {
> +		memcpy(espi_oob->dma.tx_virt, hdr + 1,
> +		       ioc->pkt_len - sizeof(*hdr));
> +		dma_wmb();
> +	} else {
> +		for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
> +			regmap_write(espi_ctrl->map,
> +				     ESPI_OOB_TX_PORT, pkt[i]);
> +	}
> +
> +	cyc = hdr->cyc;
> +	tag = hdr->tag;
> +	len = (hdr->len_h << 8) | (hdr->len_l & 0xff);
> +
> +	reg = ((cyc << ESPI_OOB_TX_CTRL_CYC_SHIFT) & ESPI_OOB_TX_CTRL_CYC_MASK)
> +		| ((tag << ESPI_OOB_TX_CTRL_TAG_SHIFT) & ESPI_OOB_TX_CTRL_TAG_MASK)
> +		| ((len << ESPI_OOB_TX_CTRL_LEN_SHIFT) & ESPI_OOB_TX_CTRL_LEN_MASK)
> +		| ESPI_OOB_TX_CTRL_TRIGGER;
> +
> +	regmap_write(espi_ctrl->map, ESPI_OOB_TX_CTRL, reg);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_mtx_n_out:
> +	mutex_unlock(&espi_oob->put_tx_mtx);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> +	struct aspeed_espi_ioc ioc;
> +	struct aspeed_espi_oob *espi_oob = container_of(
> +			fp->private_data,
> +			struct aspeed_espi_oob,
> +			mdev);
> +
> +	if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc)))
> +		return -EFAULT;
> +
> +	if (ioc.pkt_len > ESPI_PKT_LEN_MAX)
> +		return -EINVAL;
> +
> +	switch (cmd) {
> +	case ASPEED_ESPI_OOB_GET_RX:
> +		return aspeed_espi_oob_get_rx(fp, &ioc, espi_oob);
> +	case ASPEED_ESPI_OOB_PUT_TX:
> +		return aspeed_espi_oob_put_tx(fp, &ioc, espi_oob);
> +	};
> +
> +	return -EINVAL;
> +}
> +
> +void aspeed_espi_oob_event(uint32_t sts, struct aspeed_espi_oob *espi_oob)
> +{
> +	unsigned long flags;
> +
> +	if (sts & ESPI_INT_STS_OOB_RX_CMPLT) {
> +		spin_lock_irqsave(&espi_oob->lock, flags);
> +		espi_oob->rx_ready = 1;
> +		spin_unlock_irqrestore(&espi_oob->lock, flags);
> +
> +		wake_up_interruptible(&espi_oob->wq);
> +	}
> +}
> +
> +void aspeed_espi_oob_enable(struct aspeed_espi_oob *espi_oob)
> +{
> +	int i;
> +	struct aspeed_espi_oob_dma *dma = &espi_oob->dma;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl;
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_OOB_SW_RDY | ESPI_CTRL_OOB_RX_SW_RST, 0);
> +
> +	if (espi_oob->dma_mode)
> +		regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +				   ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN, 0);
> +	else
> +		regmap_write(espi_ctrl->map, ESPI_OOB_RX_CTRL, ESPI_OOB_RX_CTRL_PEND_SERV);
> +
> +	/*
> +	 * cleanup OOB RX FIFO to get rid of the data
> +	 * of OOB early init side-effect
> +	 */
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_OOB_RX_SW_RST, ESPI_CTRL_OOB_RX_SW_RST);
> +
> +	regmap_write(espi_ctrl->map, ESPI_OOB_RX_CTRL,
> +		     ESPI_OOB_RX_CTRL_PEND_SERV);
> +
> +	if (espi_oob->dma_mode) {
> +		regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +				   ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN,
> +				   ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN);
> +
> +		if (espi_ctrl->model->version == ESPI_AST2500) {
> +			regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA, dma->tx_addr);
> +			regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA, dma->rx_addr);
> +		} else {
> +			for (i = 0; i < dma->tx_desc_num; ++i)
> +				dma->tx_desc[i].data_addr = dma->tx_addr + (i * PAGE_SIZE);
> +
> +			for (i = 0; i < dma->rx_desc_num; ++i) {
> +				dma->rx_desc[i].data_addr = dma->rx_addr + (i * PAGE_SIZE);
> +				dma->rx_desc[i].dirty = 0;
> +			}
> +
> +			regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA, dma->tx_desc_addr);
> +			regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_RB_SIZE, dma->tx_desc_num);
> +
> +			regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA, dma->rx_desc_addr);
> +			regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA_RB_SIZE, dma->rx_desc_num);
> +			regmap_update_bits(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR,
> +					   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN,
> +					   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN);
> +		}
> +	}
> +
> +	regmap_write(espi_ctrl->map, ESPI_INT_STS,
> +		     ESPI_INT_STS_OOB_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
> +			   ESPI_INT_EN_OOB_BITS,
> +			   ESPI_INT_EN_OOB_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_OOB_SW_RDY,
> +			   ESPI_CTRL_OOB_SW_RDY);
> +}
> +
> +static const struct file_operations aspeed_espi_oob_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = aspeed_espi_oob_ioctl,
> +};
> +
> +void *aspeed_espi_oob_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl)
> +{
> +	int rc = 0;
> +	struct aspeed_espi_oob *espi_oob;
> +	struct aspeed_espi_oob_dma *dma;
> +
> +	espi_oob = devm_kzalloc(dev, sizeof(*espi_oob), GFP_KERNEL);
> +	if (!espi_oob)
> +		return ERR_PTR(-ENOMEM);
> +
> +	espi_oob->ctrl = espi_ctrl;
> +
> +	init_waitqueue_head(&espi_oob->wq);
> +
> +	spin_lock_init(&espi_oob->lock);
> +
> +	mutex_init(&espi_oob->put_tx_mtx);
> +	mutex_init(&espi_oob->get_rx_mtx);
> +
> +	if (of_property_read_bool(dev->of_node, "oob,dma-mode"))
> +		espi_oob->dma_mode = 1;
> +
> +	if (espi_oob->dma_mode) {
> +		dma = &espi_oob->dma;
> +
> +		/* Descriptor based OOB DMA is supported since AST2600 */
> +		if (espi_ctrl->model->version != ESPI_AST2500) {
> +			of_property_read_u32(dev->of_node, "oob,dma-tx-desc-num",
> +					     &dma->tx_desc_num);
> +			of_property_read_u32(dev->of_node, "oob,dma-rx-desc-num",
> +					     &dma->rx_desc_num);
> +
> +			if (!dma->tx_desc_num || !dma->rx_desc_num) {
> +				dev_err(dev, "invalid zero number of DMA channels\n");
> +				return ERR_PTR(-EINVAL);
> +			}
> +
> +			if (dma->tx_desc_num >= OOB_DMA_DESC_MAX_NUM ||
> +			    dma->rx_desc_num >= OOB_DMA_DESC_MAX_NUM) {
> +				dev_err(dev, "too many number of DMA channels\n");
> +				return ERR_PTR(-EINVAL);
> +			}
> +
> +			dma->tx_desc = dma_alloc_coherent(dev,
> +							  sizeof(*dma->tx_desc) * dma->tx_desc_num,
> +							  &dma->tx_desc_addr, GFP_KERNEL);

Here and a few lines below, dma_alloc_coherent() could be replaced by 
dmam_alloc_coherent() (its managed version) to simplify error handling 
and freeing of resources.

> +			if (!dma->tx_desc) {
> +				dev_err(dev, "cannot allocate DMA TX descriptor\n");
> +				return ERR_PTR(-ENOMEM);
> +			}
> +
> +			dma->rx_desc = dma_alloc_coherent(dev,
> +							  sizeof(*dma->rx_desc) * dma->rx_desc_num,
> +							  &dma->rx_desc_addr, GFP_KERNEL);
> +			if (!dma->rx_desc) {
> +				dev_err(dev, "cannot allocate DMA RX descriptor\n");
> +				return ERR_PTR(-ENOMEM);
> +			}
> +		}
> +
> +		/*
> +		 * DMA descriptors are consumed in the circular
> +		 * queue paradigm. Therefore, one dummy slot is
> +		 * reserved to detect the full condition.
> +		 *
> +		 * For AST2500 without DMA descriptors supported,
> +		 * the number of the queue slot should be 1 here.
> +		 */
> +		dma->tx_desc_num += 1;
> +		dma->rx_desc_num += 1;
> +
> +		dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->tx_desc_num,
> +						  &dma->tx_addr, GFP_KERNEL);
> +		if (!dma->tx_virt) {
> +			dev_err(dev, "cannot allocate DMA TX buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +
> +		dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->rx_desc_num,
> +						  &dma->rx_addr, GFP_KERNEL);
> +		if (!dma->rx_virt) {
> +			dev_err(dev, "cannot allocate DMA RX buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +	}
> +
> +	espi_oob->mdev.parent = dev;
> +	espi_oob->mdev.minor = MISC_DYNAMIC_MINOR;
> +	espi_oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", OOB_MDEV_NAME);
> +	espi_oob->mdev.fops = &aspeed_espi_oob_fops;
> +	rc = misc_register(&espi_oob->mdev);
> +	if (rc) {
> +		dev_err(dev, "cannot register device\n");
> +		return ERR_PTR(rc);
> +	}
> +
> +	aspeed_espi_oob_enable(espi_oob);
> +
> +	return espi_oob;
> +}
> +
> +void aspeed_espi_oob_free(struct device *dev, struct aspeed_espi_oob *espi_oob)
> +{
> +	struct aspeed_espi_oob_dma *dma = &espi_oob->dma;
> +
> +	if (espi_oob->dma_mode) {
> +		dma_free_coherent(dev, sizeof(*dma->tx_desc) * dma->tx_desc_num,
> +				  dma->tx_desc, dma->tx_desc_addr);
> +		dma_free_coherent(dev, sizeof(*dma->rx_desc) * dma->rx_desc_num,
> +				  dma->rx_desc, dma->rx_desc_addr);
> +		dma_free_coherent(dev, PAGE_SIZE * dma->tx_desc_num,
> +				  dma->tx_virt, dma->tx_addr);
> +		dma_free_coherent(dev, PAGE_SIZE * dma->rx_desc_num,
> +				  dma->rx_virt, dma->rx_addr);
> +	}
> +
> +	mutex_destroy(&espi_oob->put_tx_mtx);
> +	mutex_destroy(&espi_oob->get_rx_mtx);
> +
> +	misc_deregister(&espi_oob->mdev);
> +}
> diff --git a/drivers/soc/aspeed/aspeed-espi-oob.h b/drivers/soc/aspeed/aspeed-espi-oob.h
> new file mode 100644
> index 000000000000..03d74ef39e8b
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-oob.h
> @@ -0,0 +1,70 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2021 Aspeed Technology Inc.
> + */
> +#ifndef _ASPEED_ESPI_OOB_H_
> +#define _ASPEED_ESPI_OOB_H_
> +
> +struct oob_tx_dma_desc {
> +	uint32_t data_addr;
> +	uint8_t cyc;
> +	uint16_t tag : 4;
> +	uint16_t len : 12;
> +	uint8_t msg_type : 3;
> +	uint8_t raz0 : 1;
> +	uint8_t pec : 1;
> +	uint8_t int_en : 1;
> +	uint8_t pause : 1;
> +	uint8_t raz1 : 1;
> +	uint32_t raz2;
> +	uint32_t raz3;
> +} __packed;
> +
> +struct oob_rx_dma_desc {
> +	uint32_t data_addr;
> +	uint8_t cyc;
> +	uint16_t tag : 4;
> +	uint16_t len : 12;
> +	uint8_t raz : 7;
> +	uint8_t dirty : 1;
> +} __packed;
> +
> +struct aspeed_espi_oob_dma {
> +	uint32_t tx_desc_num;
> +	uint32_t rx_desc_num;
> +
> +	struct oob_tx_dma_desc *tx_desc;
> +	dma_addr_t tx_desc_addr;
> +
> +	struct oob_rx_dma_desc *rx_desc;
> +	dma_addr_t rx_desc_addr;
> +
> +	void *tx_virt;
> +	dma_addr_t tx_addr;
> +
> +	void *rx_virt;
> +	dma_addr_t rx_addr;
> +};
> +
> +struct aspeed_espi_oob {
> +	uint32_t dma_mode;
> +	struct aspeed_espi_oob_dma dma;
> +
> +	uint32_t rx_ready;
> +	wait_queue_head_t wq;
> +
> +	struct mutex get_rx_mtx;
> +	struct mutex put_tx_mtx;
> +
> +	spinlock_t lock;
> +
> +	struct miscdevice mdev;
> +	struct aspeed_espi_ctrl *ctrl;
> +};
> +
> +void aspeed_espi_oob_event(uint32_t sts, struct aspeed_espi_oob *espi_oob);
> +void aspeed_espi_oob_enable(struct aspeed_espi_oob *espi_oob);
> +void *aspeed_espi_oob_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl);
> +void aspeed_espi_oob_free(struct device *dev, struct aspeed_espi_oob *espi_oob);
> +
> +#endif
> diff --git a/drivers/soc/aspeed/aspeed-espi-perif.c b/drivers/soc/aspeed/aspeed-espi-perif.c
> new file mode 100644
> index 000000000000..ebf3d9978b4a
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-perif.c
> @@ -0,0 +1,511 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2021 ASPEED Technology Inc.
> + */
> +#include <linux/fs.h>
> +#include <linux/of_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> +#include <linux/miscdevice.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "aspeed-espi-ioc.h"
> +#include "aspeed-espi-ctrl.h"
> +#include "aspeed-espi-perif.h"
> +
> +#define PERIF_MDEV_NAME		"aspeed-espi-peripheral"
> +#define PERIF_MEMCYC_UNLOCK_KEY	0xfedc756e
> +#define PERIF_MEMCYC_SIZE_MIN	0x10000
> +
> +static long aspeed_espi_perif_pc_get_rx(struct file *fp,
> +					struct aspeed_espi_ioc *ioc,
> +					struct aspeed_espi_perif *espi_perif)
> +{
> +	int i, rc = 0;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	uint32_t pkt_len;
> +	struct espi_comm_hdr *hdr;
> +	unsigned long flags;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl;
> +
> +	if (fp->f_flags & O_NONBLOCK) {
> +		if (mutex_trylock(&espi_perif->pc_rx_mtx))
> +			return -EBUSY;
> +
> +		if (!espi_perif->rx_ready) {
> +			rc = -ENODATA;
> +			goto unlock_mtx_n_out;
> +		}
> +	} else {
> +		mutex_lock(&espi_perif->pc_rx_mtx);
> +
> +		if (!espi_perif->rx_ready) {
> +			rc = wait_event_interruptible(espi_perif->wq,
> +						      espi_perif->rx_ready);
> +			if (rc == -ERESTARTSYS) {
> +				rc = -EINTR;
> +				goto unlock_mtx_n_out;
> +			}
> +		}
> +	}
> +
> +	/* common header (i.e. cycle type, tag, and length) is taken by HW */
> +	regmap_read(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, &reg);
> +	cyc = (reg & ESPI_PERIF_PC_RX_CTRL_CYC_MASK) >> ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT;
> +	tag = (reg & ESPI_PERIF_PC_RX_CTRL_TAG_MASK) >> ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT;
> +	len = (reg & ESPI_PERIF_PC_RX_CTRL_LEN_MASK) >> ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT;
> +
> +	/*
> +	 * calculate the length of the rest part of the
> +	 * eSPI packet to be read from HW and copied to
> +	 * user space.
> +	 */
> +	switch (cyc) {
> +	case ESPI_PERIF_MSG:
> +		pkt_len = len + sizeof(struct espi_perif_msg);
> +		break;
> +	case ESPI_PERIF_MSG_D:
> +		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
> +			  sizeof(struct espi_perif_msg);
> +		break;
> +	case ESPI_PERIF_SUC_CMPLT_D_MIDDLE:
> +	case ESPI_PERIF_SUC_CMPLT_D_FIRST:
> +	case ESPI_PERIF_SUC_CMPLT_D_LAST:
> +	case ESPI_PERIF_SUC_CMPLT_D_ONLY:
> +		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
> +			  sizeof(struct espi_perif_cmplt);
> +		break;
> +	case ESPI_PERIF_SUC_CMPLT:
> +	case ESPI_PERIF_UNSUC_CMPLT:
> +		pkt_len = len + sizeof(struct espi_perif_cmplt);
> +		break;
> +	default:
> +		rc = -EFAULT;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	if (ioc->pkt_len < pkt_len) {
> +		rc = -EINVAL;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	pkt = vmalloc(pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_mtx_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +	hdr->cyc = cyc;
> +	hdr->tag = tag;
> +	hdr->len_h = len >> 8;
> +	hdr->len_l = len & 0xff;
> +
> +	if (espi_perif->dma_mode) {
> +		memcpy(hdr + 1, espi_perif->dma.pc_rx_virt,
> +		       pkt_len - sizeof(*hdr));
> +	} else {
> +		for (i = sizeof(*hdr); i < pkt_len; ++i) {
> +			regmap_read(espi_ctrl->map,
> +				    ESPI_PERIF_PC_RX_PORT, &reg);
> +			pkt[i] = reg & 0xff;
> +		}
> +	}
> +
> +	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	spin_lock_irqsave(&espi_perif->lock, flags);
> +
> +	regmap_write_bits(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL,
> +			  ESPI_PERIF_PC_RX_CTRL_PEND_SERV,
> +			  ESPI_PERIF_PC_RX_CTRL_PEND_SERV);
> +
> +	espi_perif->rx_ready = 0;
> +
> +	spin_unlock_irqrestore(&espi_perif->lock, flags);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_mtx_n_out:
> +	mutex_unlock(&espi_perif->pc_rx_mtx);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_perif_pc_put_tx(struct file *fp,
> +					struct aspeed_espi_ioc *ioc,
> +					struct aspeed_espi_perif *espi_perif)
> +{
> +	int i, rc = 0;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	struct espi_comm_hdr *hdr;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl;
> +
> +	if (!mutex_trylock(&espi_perif->pc_tx_mtx))
> +		return -EAGAIN;
> +
> +	regmap_read(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, &reg);
> +	if (reg & ESPI_PERIF_PC_TX_CTRL_TRIGGER) {
> +		rc = -EBUSY;
> +		goto unlock_n_out;
> +	}
> +
> +	pkt = vmalloc(ioc->pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +
> +	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	/*
> +	 * common header (i.e. cycle type, tag, and length)
> +	 * part is written to HW registers
> +	 */
> +	if (espi_perif->dma_mode) {
> +		memcpy(espi_perif->dma.pc_tx_virt, hdr + 1,
> +		       ioc->pkt_len - sizeof(*hdr));
> +		dma_wmb();
> +	} else {
> +		for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
> +			regmap_write(espi_ctrl->map,
> +				     ESPI_PERIF_PC_TX_PORT, pkt[i]);
> +	}
> +
> +	cyc = hdr->cyc;
> +	tag = hdr->tag;
> +	len = (hdr->len_h << 8) | (hdr->len_l & 0xff);
> +
> +	reg = ((cyc << ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_PC_TX_CTRL_CYC_MASK)
> +		| ((tag << ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_PC_TX_CTRL_TAG_MASK)
> +		| ((len << ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_PC_TX_CTRL_LEN_MASK)
> +		| ESPI_PERIF_PC_TX_CTRL_TRIGGER;
> +
> +	regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, reg);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_n_out:
> +	mutex_unlock(&espi_perif->pc_tx_mtx);
> +
> +	return rc;
> +}
> +
> +static long aspeed_espi_perif_np_put_tx(struct file *fp,
> +					struct aspeed_espi_ioc *ioc,
> +					struct aspeed_espi_perif *espi_perif)
> +{
> +	int i, rc = 0;
> +	uint32_t reg;
> +	uint32_t cyc, tag, len;
> +	uint8_t *pkt;
> +	struct espi_comm_hdr *hdr;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl;
> +
> +	if (!mutex_trylock(&espi_perif->np_tx_mtx))
> +		return -EAGAIN;
> +
> +	regmap_read(espi_ctrl->map, ESPI_PERIF_NP_TX_CTRL, &reg);
> +	if (reg & ESPI_PERIF_NP_TX_CTRL_TRIGGER) {
> +		rc = -EBUSY;
> +		goto unlock_n_out;
> +	}
> +
> +	pkt = vmalloc(ioc->pkt_len);
> +	if (!pkt) {
> +		rc = -ENOMEM;
> +		goto unlock_n_out;
> +	}
> +
> +	hdr = (struct espi_comm_hdr *)pkt;
> +
> +	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
> +		rc = -EFAULT;
> +		goto free_n_out;
> +	}
> +
> +	/*
> +	 * common header (i.e. cycle type, tag, and length)
> +	 * part is written to HW registers
> +	 */
> +	if (espi_perif->dma_mode) {
> +		memcpy(espi_perif->dma.np_tx_virt, hdr + 1,
> +		       ioc->pkt_len - sizeof(*hdr));
> +		dma_wmb();
> +	} else {
> +		for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
> +			regmap_write(espi_ctrl->map,
> +				     ESPI_PERIF_NP_TX_PORT, pkt[i]);
> +	}
> +
> +	cyc = hdr->cyc;
> +	tag = hdr->tag;
> +	len = (hdr->len_h << 8) | (hdr->len_l & 0xff);
> +
> +	reg = ((cyc << ESPI_PERIF_NP_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_NP_TX_CTRL_CYC_MASK)
> +		| ((tag << ESPI_PERIF_NP_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_NP_TX_CTRL_TAG_MASK)
> +		| ((len << ESPI_PERIF_NP_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_NP_TX_CTRL_LEN_MASK)
> +		| ESPI_PERIF_NP_TX_CTRL_TRIGGER;
> +
> +	regmap_write(espi_ctrl->map, ESPI_PERIF_NP_TX_CTRL, reg);
> +
> +free_n_out:
> +	vfree(pkt);
> +
> +unlock_n_out:
> +	mutex_unlock(&espi_perif->np_tx_mtx);
> +
> +	return rc;
> +
> +}
> +
> +static long aspeed_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> +	struct aspeed_espi_ioc ioc;
> +	struct aspeed_espi_perif *espi_perif = container_of(
> +			fp->private_data,
> +			struct aspeed_espi_perif,
> +			mdev);
> +
> +	if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc)))
> +		return -EFAULT;
> +
> +	if (ioc.pkt_len > ESPI_PKT_LEN_MAX)
> +		return -EINVAL;
> +
> +	switch (cmd) {
> +	case ASPEED_ESPI_PERIF_PC_GET_RX:
> +		return aspeed_espi_perif_pc_get_rx(fp, &ioc, espi_perif);
> +	case ASPEED_ESPI_PERIF_PC_PUT_TX:
> +		return aspeed_espi_perif_pc_put_tx(fp, &ioc, espi_perif);
> +	case ASPEED_ESPI_PERIF_NP_PUT_TX:
> +		return aspeed_espi_perif_np_put_tx(fp, &ioc, espi_perif);
> +	};
> +
> +	return -EINVAL;
> +}
> +
> +static int aspeed_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma)
> +{
> +	struct aspeed_espi_perif *espi_perif = container_of(
> +			fp->private_data,
> +			struct aspeed_espi_perif,
> +			mdev);
> +	unsigned long vm_size = vma->vm_end - vma->vm_start;
> +	pgprot_t prot = vma->vm_page_prot;
> +
> +	if (!espi_perif->mcyc_enable)
> +		return -EPERM;
> +
> +	if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > espi_perif->mcyc_size)
> +		return -EINVAL;
> +
> +	prot = pgprot_noncached(prot);
> +
> +	if (remap_pfn_range(vma, vma->vm_start,
> +			    (espi_perif->mcyc_taddr >> PAGE_SHIFT) + vma->vm_pgoff,
> +			    vm_size, prot))
> +		return -EAGAIN;
> +
> +	return 0;
> +}
> +
> +void aspeed_espi_perif_event(uint32_t sts, struct aspeed_espi_perif *espi_perif)
> +{
> +	unsigned long flags;
> +
> +	if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) {
> +		spin_lock_irqsave(&espi_perif->lock, flags);
> +		espi_perif->rx_ready = 1;
> +		spin_unlock_irqrestore(&espi_perif->lock, flags);
> +
> +		wake_up_interruptible(&espi_perif->wq);
> +	}
> +}
> +
> +void aspeed_espi_perif_enable(struct aspeed_espi_perif *espi_perif)
> +{
> +	struct aspeed_espi_perif_dma *dma = &espi_perif->dma;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl;
> +
> +	if (espi_perif->mcyc_enable) {
> +		if (espi_ctrl->model->version == ESPI_AST2500) {
> +			regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK,
> +				     PERIF_MEMCYC_UNLOCK_KEY);
> +			regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK,
> +				     espi_perif->mcyc_mask);
> +		} else {
> +			regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK,
> +				     espi_perif->mcyc_mask | ESPI_PERIF_PC_RX_MASK_CFG_WP);
> +			regmap_update_bits(espi_ctrl->map, ESPI_CTRL2,
> +					   ESPI_CTRL2_MEMCYC_RD_DIS | ESPI_CTRL2_MEMCYC_WR_DIS, 0);
> +		}
> +
> +		regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_SADDR, espi_perif->mcyc_saddr);
> +		regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_TADDR, espi_perif->mcyc_taddr);
> +	}
> +
> +	if (espi_perif->dma_mode) {
> +		regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_DMA, dma->pc_rx_addr);
> +		regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_DMA, dma->pc_tx_addr);
> +		regmap_write(espi_ctrl->map, ESPI_PERIF_NP_TX_DMA, dma->np_tx_addr);
> +
> +		regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +				   ESPI_CTRL_PERIF_NP_TX_DMA_EN |
> +				   ESPI_CTRL_PERIF_PC_TX_DMA_EN |
> +				   ESPI_CTRL_PERIF_PC_RX_DMA_EN,
> +				   ESPI_CTRL_PERIF_NP_TX_DMA_EN |
> +				   ESPI_CTRL_PERIF_PC_TX_DMA_EN |
> +				   ESPI_CTRL_PERIF_PC_RX_DMA_EN);
> +	}
> +
> +	regmap_write(espi_ctrl->map, ESPI_INT_STS,
> +		     ESPI_INT_STS_PERIF_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
> +			   ESPI_INT_EN_PERIF_BITS,
> +			   ESPI_INT_EN_PERIF_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_PERIF_SW_RDY,
> +			   ESPI_CTRL_PERIF_SW_RDY);
> +}
> +
> +static const struct file_operations aspeed_espi_perif_fops = {
> +	.owner = THIS_MODULE,
> +	.mmap = aspeed_espi_perif_mmap,
> +	.unlocked_ioctl = aspeed_espi_perif_ioctl,
> +};
> +
> +void *aspeed_espi_perif_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl)
> +{
> +	int rc;
> +	struct aspeed_espi_perif *espi_perif;
> +	struct aspeed_espi_perif_dma *dma;
> +
> +	espi_perif = devm_kzalloc(dev, sizeof(*espi_perif), GFP_KERNEL);
> +	if (!espi_perif)
> +		return ERR_PTR(-ENOMEM);
> +
> +	espi_perif->ctrl = espi_ctrl;
> +
> +	init_waitqueue_head(&espi_perif->wq);
> +
> +	spin_lock_init(&espi_perif->lock);
> +
> +	mutex_init(&espi_perif->pc_rx_mtx);
> +	mutex_init(&espi_perif->pc_tx_mtx);
> +	mutex_init(&espi_perif->np_tx_mtx);
> +
> +	espi_perif->mcyc_enable = of_property_read_bool(dev->of_node, "perif,memcyc-enable");
> +	if (espi_perif->mcyc_enable) {
> +		rc = of_property_read_u32(dev->of_node, "perif,memcyc-src-addr",
> +					  &espi_perif->mcyc_saddr);
> +		if (rc) {
> +			dev_err(dev, "cannot get Host source address for memory cycle\n");
> +			return ERR_PTR(-ENODEV);
> +		}
> +
> +		rc = of_property_read_u32(dev->of_node, "perif,memcyc-size",
> +					  &espi_perif->mcyc_size);
> +		if (rc) {
> +			dev_err(dev, "cannot get size for memory cycle\n");
> +			return ERR_PTR(-ENODEV);
> +		}
> +
> +		if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN)
> +			espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN;
> +		else
> +			espi_perif->mcyc_size = roundup_pow_of_two(espi_perif->mcyc_size);
> +
> +		espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1);
> +		espi_perif->mcyc_virt = dma_alloc_coherent(dev, espi_perif->mcyc_size,
> +							   &espi_perif->mcyc_taddr, GFP_KERNEL);

Here and a few lines below, dma_alloc_coherent() could be replaced by 
dmam_alloc_coherent() (its managed version) to simplify error handling 
and freeing of resources.


> +		if (!espi_perif->mcyc_virt) {
> +			dev_err(dev, "cannot allocate memory cycle region\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +	}
> +
> +	if (of_property_read_bool(dev->of_node, "perif,dma-mode")) {
> +		dma = &espi_perif->dma;
> +
> +		dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> +						     &dma->pc_tx_addr, GFP_KERNEL);
> +		if (!dma->pc_tx_virt) {
> +			dev_err(dev, "cannot allocate posted TX DMA buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +
> +		dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> +						     &dma->pc_rx_addr, GFP_KERNEL);
> +		if (!dma->pc_rx_virt) {
> +			dev_err(dev, "cannot allocate posted RX DMA buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +
> +		dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> +				&dma->np_tx_addr, GFP_KERNEL);
> +		if (!dma->np_tx_virt) {
> +			dev_err(dev, "cannot allocate non-posted TX DMA buffer\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +
> +		espi_perif->dma_mode = 1;
> +	}
> +
> +	espi_perif->mdev.parent = dev;
> +	espi_perif->mdev.minor = MISC_DYNAMIC_MINOR;
> +	espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", PERIF_MDEV_NAME);
> +	espi_perif->mdev.fops = &aspeed_espi_perif_fops;
> +	rc = misc_register(&espi_perif->mdev);
> +	if (rc) {
> +		dev_err(dev, "cannot register device\n");
> +		return ERR_PTR(rc);
> +	}
> +
> +	aspeed_espi_perif_enable(espi_perif);
> +
> +	return espi_perif;
> +}
> +
> +void aspeed_espi_perif_free(struct device *dev, struct aspeed_espi_perif *espi_perif)
> +{
> +	struct aspeed_espi_perif_dma *dma = &espi_perif->dma;
> +
> +	if (espi_perif->mcyc_virt)
> +		dma_free_coherent(dev, espi_perif->mcyc_size,
> +				  espi_perif->mcyc_virt,
> +				  espi_perif->mcyc_taddr);
> +
> +	if (espi_perif->dma_mode) {
> +		dma_free_coherent(dev, PAGE_SIZE, dma->pc_tx_virt,
> +				  dma->pc_tx_addr);
> +		dma_free_coherent(dev, PAGE_SIZE, dma->pc_rx_virt,
> +				  dma->pc_rx_addr);
> +		dma_free_coherent(dev, PAGE_SIZE, dma->np_tx_virt,
> +				  dma->np_tx_addr);
> +	}
> +
> +	mutex_destroy(&espi_perif->pc_tx_mtx);
> +	mutex_destroy(&espi_perif->np_tx_mtx);
> +
> +	misc_deregister(&espi_perif->mdev);
> +}
> diff --git a/drivers/soc/aspeed/aspeed-espi-perif.h b/drivers/soc/aspeed/aspeed-espi-perif.h
> new file mode 100644
> index 000000000000..1b964e4680f5
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-perif.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2021 ASPEED Technology Inc.
> + */
> +#ifndef _ASPEED_ESPI_PERIF_H_
> +#define _ASPEED_ESPI_PERIF_H_
> +
> +struct aspeed_espi_perif_dma {
> +	void *pc_tx_virt;
> +	dma_addr_t pc_tx_addr;
> +	void *pc_rx_virt;
> +	dma_addr_t pc_rx_addr;
> +	void *np_tx_virt;
> +	dma_addr_t np_tx_addr;
> +};
> +
> +struct aspeed_espi_perif {
> +	uint32_t mcyc_enable;
> +	void *mcyc_virt;
> +	uint32_t mcyc_saddr;
> +	phys_addr_t mcyc_taddr;
> +	uint32_t mcyc_size;
> +	uint32_t mcyc_mask;
> +
> +	uint32_t dma_mode;
> +	struct aspeed_espi_perif_dma dma;
> +
> +	uint32_t rx_ready;
> +	wait_queue_head_t wq;
> +
> +	spinlock_t lock;
> +	struct mutex pc_rx_mtx;
> +	struct mutex pc_tx_mtx;
> +	struct mutex np_tx_mtx;
> +
> +	struct miscdevice mdev;
> +	struct aspeed_espi_ctrl *ctrl;
> +};
> +
> +void aspeed_espi_perif_event(uint32_t sts, struct aspeed_espi_perif *espi_perif);
> +void aspeed_espi_perif_enable(struct aspeed_espi_perif *espi_perif);
> +void *aspeed_espi_perif_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl);
> +void aspeed_espi_perif_free(struct device *dev, struct aspeed_espi_perif *espi_perif);
> +
> +#endif
> diff --git a/drivers/soc/aspeed/aspeed-espi-vw.c b/drivers/soc/aspeed/aspeed-espi-vw.c
> new file mode 100644
> index 000000000000..2bdfedfea12e
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-vw.c
> @@ -0,0 +1,142 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2021 ASPEED Technology Inc.
> + */
> +#include <linux/fs.h>
> +#include <linux/of_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> +#include <linux/miscdevice.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "aspeed-espi-ioc.h"
> +#include "aspeed-espi-ctrl.h"
> +#include "aspeed-espi-vw.h"
> +
> +#define VW_MDEV_NAME	"aspeed-espi-vw"
> +
> +static long aspeed_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> +	uint32_t val;
> +
> +	struct aspeed_espi_vw *espi_vw = container_of(fp->private_data,
> +						      struct aspeed_espi_vw,
> +						      mdev);
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl;
> +
> +	switch (cmd) {
> +	case ASPEED_ESPI_VW_GET_GPIO_VAL:
> +		regmap_read(espi_ctrl->map, ESPI_VW_GPIO_VAL, &val);
> +		if (put_user(val, (uint32_t __user *)arg))
> +			return -EFAULT;
> +		break;
> +
> +	case ASPEED_ESPI_VW_PUT_GPIO_VAL:
> +		if (get_user(val, (uint32_t __user *)arg))
> +			return -EFAULT;
> +		regmap_write(espi_ctrl->map, ESPI_VW_GPIO_VAL, val);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	};
> +
> +	return 0;
> +}
> +
> +void aspeed_espi_vw_event(uint32_t sts, struct aspeed_espi_vw *espi_vw)
> +{
> +	uint32_t sysevt_sts;
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl;
> +
> +	regmap_read(espi_ctrl->map, ESPI_INT_STS, &sts);
> +
> +	if (sts & ESPI_INT_STS_VW_SYSEVT) {
> +		regmap_read(espi_ctrl->map, ESPI_SYSEVT_INT_STS, &sysevt_sts);
> +
> +		if (espi_ctrl->model->version == ESPI_AST2500) {
> +			if (sysevt_sts & ESPI_SYSEVT_INT_STS_HOST_RST_WARN)
> +				regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT,
> +						   ESPI_SYSEVT_HOST_RST_ACK,
> +						   ESPI_SYSEVT_HOST_RST_ACK);
> +
> +			if (sysevt_sts & ESPI_SYSEVT_INT_STS_OOB_RST_WARN)
> +				regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT,
> +						   ESPI_SYSEVT_OOB_RST_ACK,
> +						   ESPI_SYSEVT_OOB_RST_ACK);
> +		}
> +
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_STS, sysevt_sts);
> +	}
> +
> +	if (sts & ESPI_INT_STS_VW_SYSEVT1) {
> +		regmap_read(espi_ctrl->map, ESPI_SYSEVT1_INT_STS, &sysevt_sts);
> +
> +		if (sysevt_sts & ESPI_SYSEVT1_INT_STS_SUSPEND_WARN)
> +			regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1,
> +					   ESPI_SYSEVT1_SUSPEND_ACK,
> +					   ESPI_SYSEVT1_SUSPEND_ACK);
> +
> +		regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_STS, sysevt_sts);
> +	}
> +}
> +
> +void aspeed_espi_vw_enable(struct aspeed_espi_vw *espi_vw)
> +{
> +	struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl;
> +
> +	regmap_write(espi_ctrl->map, ESPI_INT_STS,
> +		     ESPI_INT_STS_VW_BITS);
> +
> +	regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
> +			   ESPI_INT_EN_VW_BITS,
> +			   ESPI_INT_EN_VW_BITS);
> +
> +	/*
> +	 * Enforce VW GPIO to SW mode due to security concern.
> +	 * The HW mode allows the Host to manipulate BMC GPIOs
> +	 * without notififications.
> +	 */
> +	regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
> +			   ESPI_CTRL_VW_GPIO_SW_MODE | ESPI_CTRL_VW_SW_RDY,
> +			   ESPI_CTRL_VW_GPIO_SW_MODE | ESPI_CTRL_VW_SW_RDY);
> +}
> +
> +static const struct file_operations aspeed_espi_vw_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = aspeed_espi_vw_ioctl,
> +};
> +
> +void *aspeed_espi_vw_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl)
> +{
> +	int rc;
> +	struct aspeed_espi_vw *espi_vw;
> +
> +	espi_vw = devm_kzalloc(dev, sizeof(*espi_vw), GFP_KERNEL);
> +	if (!espi_vw)
> +		return ERR_PTR(-ENOMEM);
> +
> +	espi_vw->ctrl = espi_ctrl;
> +
> +	espi_vw->mdev.parent = dev;
> +	espi_vw->mdev.minor = MISC_DYNAMIC_MINOR;
> +	espi_vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", VW_MDEV_NAME);
> +	espi_vw->mdev.fops = &aspeed_espi_vw_fops;
> +	rc = misc_register(&espi_vw->mdev);
> +	if (rc) {
> +		dev_err(dev, "cannot register device\n");
> +		return ERR_PTR(rc);
> +	}
> +
> +	aspeed_espi_vw_enable(espi_vw);
> +
> +	return espi_vw;
> +}
> +
> +void aspeed_espi_vw_free(struct device *dev, struct aspeed_espi_vw *espi_vw)
> +{
> +	misc_deregister(&espi_vw->mdev);
> +}
> diff --git a/drivers/soc/aspeed/aspeed-espi-vw.h b/drivers/soc/aspeed/aspeed-espi-vw.h
> new file mode 100644
> index 000000000000..aba9c414ac1b
> --- /dev/null
> +++ b/drivers/soc/aspeed/aspeed-espi-vw.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2021 ASPEED Technology Inc.
> + */
> +#ifndef _ASPEED_ESPI_VW_H_
> +#define _ASPEED_ESPI_VW_H_
> +
> +struct aspeed_espi_vw {
> +	int irq;
> +	int irq_reset;
> +
> +	struct miscdevice mdev;
> +	struct aspeed_espi_ctrl *ctrl;
> +};
> +
> +void aspeed_espi_vw_event(uint32_t sts, struct aspeed_espi_vw *espi_vw);
> +void aspeed_espi_vw_enable(struct aspeed_espi_vw *espi_vw);
> +void *aspeed_espi_vw_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl);
> +void aspeed_espi_vw_free(struct device *dev, struct aspeed_espi_vw *espi_vw);
> +
> +#endif

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ