> From: Christophe JAILLET <christophe.jaillet@xxxxxxxxxx> > Sent: Thursday, May 19, 2022 3:13 AM > > 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@xxxxxxxxxxxxxxxx> > > Reported-by: kernel test robot > <lkp-ral2JQCrhuEAvxtiuMwx3w@xxxxxxxxxxxxxxxx> > > --- > > 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()? Thanks for reminding. The clock shall be disabled in .remove function. > > > + > > + 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@xxxxxxxxxxxxxxxx>"); > > +MODULE_AUTHOR("Ryan Chen > <ryan_chen-SAlXDmAnmOAqDJ6do+/SaQ@xxxxxxxxxxxxxxxx>"); > > +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, ®); > > + 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, ®); > > + 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, ®); > > + 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. Will revise as suggested. Thanks for the review. Chiawei > > > + 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, ®); > > + 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, ®); > > + 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, ®); > > + 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, ®); > > + 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, ®); > > + 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, ®); > > + 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, ®); > > + 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, ®); > > + 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