This patch series add the driver support for the eSPI controller
of Aspeed 5/6th generation SoCs. This controller is a slave device
communicating with a master over Enhanced Serial Peripheral Interface (eSPI).
It supports all of the 4 eSPI channels, namely peripheral, virtual wire,
out-of-band, and flash, and operates at max frequency of 66MHz.
v3:
- remove the redundant patch "clk: aspeed: Add eSPI reset bit"
- fix missing header inclusion reported by test bot
- fix dt-bindings error reported by yamllint
v2:
- remove irqchip implementation
- merge per-channel drivers into single one to avoid the racing issue
among eSPI handshake process and driver probing.
Chia-Wei Wang (4):
dt-bindings: aspeed: Add eSPI controller
MAINTAINER: Add ASPEED eSPI driver entry
soc: aspeed: Add eSPI driver
ARM: dts: aspeed: Add eSPI node
.../devicetree/bindings/soc/aspeed/espi.yaml | 157 +++++
MAINTAINERS | 9 +
arch/arm/boot/dts/aspeed-g6.dtsi | 17 +
drivers/soc/aspeed/Kconfig | 11 +
drivers/soc/aspeed/Makefile | 1 +
drivers/soc/aspeed/aspeed-espi-ctrl.c | 205 ++++++
drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 +++++++++
drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++
drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++
drivers/soc/aspeed/aspeed-espi-oob.h | 611 ++++++++++++++++++
drivers/soc/aspeed/aspeed-espi-perif.h | 539 +++++++++++++++
drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++
12 files changed, 2529 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml
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.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h
--
2.17.1
Add Ryan Chen and myself as the maintainer of Aspeed eSPI
driver. Joel Stanley is added as the reviewer.
Signed-off-by: Chia-Wei Wang <[email protected]>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index fd25e4ecf0b9..b21bcb46692e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1713,6 +1713,15 @@ F: drivers/crypto/axis
F: drivers/mmc/host/usdhi6rol0.c
F: drivers/pinctrl/pinctrl-artpec*
+ARM/ASPEED ESPI DRIVER
+M: Chia-Wei Wang <[email protected]>
+M: Ryan Chen <[email protected]>
+R: Joel Stanley <[email protected]>
+L: [email protected] (moderated for non-subscribers)
+L: [email protected] (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/soc/aspeed/espi.yaml
+
ARM/ASPEED I2C DRIVER
M: Brendan Higgins <[email protected]>
R: Benjamin Herrenschmidt <[email protected]>
--
2.17.1
Add dt-bindings for Aspeed eSPI controller
Signed-off-by: Chia-Wei Wang <[email protected]>
---
.../devicetree/bindings/soc/aspeed/espi.yaml | 157 ++++++++++++++++++
1 file changed, 157 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml
diff --git a/Documentation/devicetree/bindings/soc/aspeed/espi.yaml b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml
new file mode 100644
index 000000000000..f8948efde379
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# # Copyright (c) 2021 Aspeed Technology Inc.
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/soc/aspeed/espi.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Aspeed eSPI Controller
+
+maintainers:
+ - Chia-Wei Wang <[email protected]>
+ - Ryan Chen <[email protected]>
+
+description:
+ Aspeed eSPI controller implements a slave side eSPI endpoint device
+ supporting the four eSPI channels, namely peripheral, virtual wire,
+ out-of-band, and flash.
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - aspeed,ast2500-espi
+ - aspeed,ast2600-espi
+ - const: simple-mfd
+ - const: syscon
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 1
+
+ ranges: true
+
+ espi-ctrl:
+ type: object
+
+ properties:
+ compatible:
+ items:
+ - enum:
+ - aspeed,ast2500-espi-ctrl
+ - aspeed,ast2600-espi-ctrl
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ perif,memcyc-enable:
+ type: boolean
+ description: Enable memory cycle over eSPI peripheral channel
+
+ perif,memcyc-src-addr:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The Host side address to be decoded into the memory cycle over eSPI peripheral channel
+
+ perif,memcyc-size:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The size of the memory region allocated for the memory cycle over eSPI peripheral channel
+ minimum: 65536
+
+ perif,dma-mode:
+ type: boolean
+ description: Enable DMA support for eSPI peripheral channel
+
+ oob,dma-mode:
+ type: boolean
+ description: Enable DMA support for eSPI out-of-band channel
+
+ oob,dma-tx-desc-num:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 2
+ maximum: 1023
+ description: The number of TX descriptors available for eSPI OOB DMA engine
+
+ oob,dma-rx-desc-num:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 2
+ maximum: 1023
+ description: The number of RX descriptors available for eSPI OOB DMA engine
+
+ flash,dma-mode:
+ type: boolean
+ description: Enable DMA support for eSPI flash channel
+
+ flash,safs-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [ 0, 1, 2 ]
+ default: 0
+ description: Slave-Attached-Sharing-Flash mode, 0->Mix, 1->SW, 2->HW
+
+ dependencies:
+ perif,memcyc-src-addr: [ "perif,memcyc-enable" ]
+ perif,memcyc-size: [ "perif,memcyc-enable" ]
+ oob,dma-tx-desc-num: [ "oob,dma-mode" ]
+ oob,dma-rx-desc-num: [ "oob,dma-mode" ]
+
+ required:
+ - compatible
+ - interrupts
+ - clocks
+
+ espi-mmbi:
+ type: object
+
+ properties:
+ compatible:
+ const: aspeed,ast2600-espi-mmbi
+
+ interrupts:
+ maxItems: 1
+
+ required:
+ - compatible
+ - interrupts
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
+
+ espi: espi@1e6ee000 {
+ compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon";
+ reg = <0x1e6ee000 0x1000>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e6ee000 0x1000>;
+
+ espi_ctrl: espi-ctrl@0 {
+ compatible = "aspeed,ast2600-espi-ctrl";
+ reg = <0x0 0x800>;
+ interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
+ };
+
+ espi_mmbi: espi-mmbi@800 {
+ compatible = "aspeed,ast2600-espi-mmbi";
+ reg = <0x800 0x50>;
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
--
2.17.1
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 <[email protected]>
Reported-by: kernel test robot <[email protected]>
---
drivers/soc/aspeed/Kconfig | 11 +
drivers/soc/aspeed/Makefile | 1 +
drivers/soc/aspeed/aspeed-espi-ctrl.c | 205 +++++++++
drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 ++++++++++++
drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++
drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++++
drivers/soc/aspeed/aspeed-espi-oob.h | 611 +++++++++++++++++++++++++
drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++
drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++++
9 files changed, 2346 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.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h
create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index 243ca196e6ad..7617a02df5cf 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -42,6 +42,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 fcab7192e1a4..650d8de2875d 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.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
diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.c b/drivers/soc/aspeed/aspeed-espi-ctrl.c
new file mode 100644
index 000000000000..2b95967d9d30
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-ctrl.c
@@ -0,0 +1,205 @@
+// 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 alloc/free/event/ioctl handlers of the eSPI 4 channels */
+#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->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->version = (uint32_t)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;
+ }
+
+ 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 of_device_id aspeed_espi_ctrl_of_matches[] = {
+ { .compatible = "aspeed,ast2500-espi-ctrl", .data = (void *)ESPI_AST2500 },
+ { .compatible = "aspeed,ast2600-espi-ctrl", .data = (void *)ESPI_AST2600 },
+ { },
+};
+
+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 <[email protected]>");
+MODULE_AUTHOR("Ryan Chen <[email protected]>");
+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..44b93698fd0f
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-ctrl.h
@@ -0,0 +1,304 @@
+/* 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_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;
+
+ uint32_t version;
+};
+
+/* 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_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.h b/drivers/soc/aspeed/aspeed-espi-flash.h
new file mode 100644
index 000000000000..1acb9877cb2c
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-flash.h
@@ -0,0 +1,380 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 ASPEED Technology Inc.
+ *
+ * This header includes the data strcuture and the handler
+ * for eSPI flash channel. It is part of Aspeed eSPI slave
+ * driver implementation
+ */
+#ifndef _ASPEED_ESPI_FLASH_H_
+#define _ASPEED_ESPI_FLASH_H_
+
+#define FLASH_MDEV_NAME "aspeed-espi-flash"
+
+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;
+};
+
+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;
+}
+
+static const struct file_operations aspeed_espi_flash_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = aspeed_espi_flash_ioctl,
+};
+
+static 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);
+ }
+}
+
+static 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 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) {
+ 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;
+}
+
+static 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);
+}
+
+#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..e93739c80b5f
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-ioc.h
@@ -0,0 +1,153 @@
+/* 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
+
+/* peripheral channel (ch0) */
+#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)
+/* peripheral channel (ch1) */
+#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) */
+#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) */
+#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.h b/drivers/soc/aspeed/aspeed-espi-oob.h
new file mode 100644
index 000000000000..182a9ca1e4cc
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-oob.h
@@ -0,0 +1,611 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Aspeed Technology Inc.
+ *
+ * This header includes the data strcuture and the handler
+ * for eSPI out-of-band channel. It is part of Aspeed eSPI
+ * slave driver implementation
+ */
+#ifndef _ASPEED_ESPI_OOB_H_
+#define _ASPEED_ESPI_OOB_H_
+
+#define OOB_MDEV_NAME "aspeed-espi-oob"
+
+/* DMA descriptor is supported since AST2600 */
+#define OOB_DMA_DESC_MAX_NUM 1024
+
+/* DMA TX descriptor type */
+#define OOB_DMA_TX_DESC_CUST 0x04
+
+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;
+};
+
+/* 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->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)
+ return -EBUSY;
+
+ 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->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;
+}
+
+static const struct file_operations aspeed_espi_oob_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = aspeed_espi_oob_ioctl,
+};
+
+static 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);
+ }
+}
+
+static 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->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 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->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);
+ 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;
+}
+
+static 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);
+}
+
+#endif
diff --git a/drivers/soc/aspeed/aspeed-espi-perif.h b/drivers/soc/aspeed/aspeed-espi-perif.h
new file mode 100644
index 000000000000..0bdd71ee828a
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-perif.h
@@ -0,0 +1,539 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 ASPEED Technology Inc.
+ *
+ * This header includes the data strcuture and the handler
+ * for eSPI peripheral channel. It is part of Aspeed eSPI
+ * slave driver implementation
+ */
+#ifndef _ASPEED_ESPI_PERIF_H_
+#define _ASPEED_ESPI_PERIF_H_
+
+#define PERIF_MDEV_NAME "aspeed-espi-peripheral"
+#define PERIF_MEMCYC_UNLOCK_KEY 0xfedc756e
+#define PERIF_MEMCYC_SIZE_MIN 0x10000
+
+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;
+ phys_addr_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;
+};
+
+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;
+}
+
+static const struct file_operations aspeed_espi_perif_fops = {
+ .owner = THIS_MODULE,
+ .mmap = aspeed_espi_perif_mmap,
+ .unlocked_ioctl = aspeed_espi_perif_ioctl,
+};
+
+static 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);
+ }
+}
+
+static 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->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 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);
+ 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;
+}
+
+static 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);
+}
+
+#endif
diff --git a/drivers/soc/aspeed/aspeed-espi-vw.h b/drivers/soc/aspeed/aspeed-espi-vw.h
new file mode 100644
index 000000000000..3d49366c912e
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-vw.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 ASPEED Technology Inc.
+ *
+ * This header includes the data strcuture and the handler
+ * for eSPI virtual wire channel. It is part of Aspeed eSPI
+ * slave driver implementation
+ */
+#ifndef _ASPEED_ESPI_VW_H_
+#define _ASPEED_ESPI_VW_H_
+
+#define VW_MDEV_NAME "aspeed-espi-vw"
+
+struct aspeed_espi_vw {
+ int irq;
+ int irq_reset;
+
+ struct miscdevice mdev;
+ struct aspeed_espi_ctrl *ctrl;
+};
+
+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;
+}
+
+static const struct file_operations aspeed_espi_vw_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = aspeed_espi_vw_ioctl,
+};
+
+static 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->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);
+ }
+}
+
+static 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);
+
+ regmap_update_bits(espi_ctrl->map, ESPI_CTRL,
+ ESPI_CTRL_VW_SW_RDY,
+ ESPI_CTRL_VW_SW_RDY);
+}
+
+static 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;
+}
+
+static void aspeed_espi_vw_free(struct device *dev, struct aspeed_espi_vw *espi_vw)
+{
+ misc_deregister(&espi_vw->mdev);
+}
+
+#endif
--
2.17.1
Add eSPI to the device tree for Aspeed 5/6th generation SoCs.
Signed-off-by: Chia-Wei Wang <[email protected]>
---
arch/arm/boot/dts/aspeed-g6.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index f96607b7b4e2..47dc0b3993d1 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -364,6 +364,23 @@
status = "disabled";
};
+ espi: espi@1e6ee000 {
+ compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon";
+ reg = <0x1e6ee000 0x1000>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e6ee000 0x1000>;
+
+ espi_ctrl: espi-ctrl@0 {
+ compatible = "aspeed,ast2600-espi-ctrl";
+ reg = <0x0 0x800>;
+ interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
+ status = "disabled";
+ };
+ };
+
gpio0: gpio@1e780000 {
#gpio-cells = <2>;
gpio-controller;
--
2.17.1
On Thu, 26 Aug 2021 14:16:20 +0800, Chia-Wei Wang wrote:
> Add dt-bindings for Aspeed eSPI controller
>
> Signed-off-by: Chia-Wei Wang <[email protected]>
> ---
> .../devicetree/bindings/soc/aspeed/espi.yaml | 157 ++++++++++++++++++
> 1 file changed, 157 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml
>
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/aspeed/espi.example.dt.yaml: espi@1e6ee000: 'espi-ctrl@0', 'espi-mmbi@800' do not match any of the regexes: 'pinctrl-[0-9]+'
From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/aspeed/espi.yaml
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/patch/1520968
This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit.
Hi Chia-Wei,
I love your patch! Perhaps something to improve:
[auto build test WARNING on robh/for-next]
[also build test WARNING on arm/for-next keystone/next soc/for-next rockchip/for-next arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc7 next-20210826]
[cannot apply to xlnx/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arm64-randconfig-r025-20210826 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project ea08c4cd1c0869ec5024a8bb3f5cdf06ab03ae83)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# https://github.com/0day-ci/linux/commit/2980a1777c50754fe145f2e73ded8739931c0712
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737
git checkout 2980a1777c50754fe145f2e73ded8739931c0712
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All warnings (new ones prefixed by >>):
In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:22:
drivers/soc/aspeed/aspeed-espi-perif.h:446:8: error: incompatible pointer types passing 'phys_addr_t *' (aka 'unsigned long long *') to parameter of type 'u32 *' (aka 'unsigned int *') [-Werror,-Wincompatible-pointer-types]
&espi_perif->mcyc_saddr);
^~~~~~~~~~~~~~~~~~~~~~~
include/linux/of.h:1249:17: note: passing argument to parameter 'out_value' here
u32 *out_value)
^
>> drivers/soc/aspeed/aspeed-espi-ctrl.c:98:23: warning: cast to smaller integer type 'uint32_t' (aka 'unsigned int') from 'const void *' [-Wvoid-pointer-to-int-cast]
espi_ctrl->version = (uint32_t)of_device_get_match_data(dev);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning and 1 error generated.
vim +98 drivers/soc/aspeed/aspeed-espi-ctrl.c
87
88 static int aspeed_espi_ctrl_probe(struct platform_device *pdev)
89 {
90 int rc = 0;
91 struct aspeed_espi_ctrl *espi_ctrl;
92 struct device *dev = &pdev->dev;
93
94 espi_ctrl = devm_kzalloc(dev, sizeof(*espi_ctrl), GFP_KERNEL);
95 if (!espi_ctrl)
96 return -ENOMEM;
97
> 98 espi_ctrl->version = (uint32_t)of_device_get_match_data(dev);
99
100 espi_ctrl->map = syscon_node_to_regmap(dev->parent->of_node);
101 if (IS_ERR(espi_ctrl->map)) {
102 dev_err(dev, "cannot get remap\n");
103 return -ENODEV;
104 }
105
106 espi_ctrl->irq = platform_get_irq(pdev, 0);
107 if (espi_ctrl->irq < 0)
108 return espi_ctrl->irq;
109
110 espi_ctrl->clk = devm_clk_get(dev, NULL);
111 if (IS_ERR(espi_ctrl->clk)) {
112 dev_err(dev, "cannot get clock\n");
113 return -ENODEV;
114 }
115
116 rc = clk_prepare_enable(espi_ctrl->clk);
117 if (rc) {
118 dev_err(dev, "cannot enable clock\n");
119 return rc;
120 }
121
122 espi_ctrl->perif = aspeed_espi_perif_alloc(dev, espi_ctrl);
123 if (IS_ERR(espi_ctrl->perif)) {
124 dev_err(dev, "failed to allocate peripheral channel\n");
125 return PTR_ERR(espi_ctrl->perif);
126 }
127
128 espi_ctrl->vw = aspeed_espi_vw_alloc(dev, espi_ctrl);
129 if (IS_ERR(espi_ctrl->vw)) {
130 dev_err(dev, "failed to allocate virtual wire channel\n");
131 return PTR_ERR(espi_ctrl->vw);
132 }
133
134 espi_ctrl->oob = aspeed_espi_oob_alloc(dev, espi_ctrl);
135 if (IS_ERR(espi_ctrl->oob)) {
136 dev_err(dev, "failed to allocate out-of-band channel\n");
137 return PTR_ERR(espi_ctrl->oob);
138 }
139
140 espi_ctrl->flash = aspeed_espi_flash_alloc(dev, espi_ctrl);
141 if (rc) {
142 dev_err(dev, "failed to allocate flash channel\n");
143 return PTR_ERR(espi_ctrl->flash);
144 }
145
146 regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0);
147 regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0);
148 regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff);
149
150 regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1);
151 regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1);
152
153 rc = devm_request_irq(dev, espi_ctrl->irq,
154 aspeed_espi_ctrl_isr,
155 0, DEVICE_NAME, espi_ctrl);
156 if (rc) {
157 dev_err(dev, "failed to request IRQ\n");
158 return rc;
159 }
160
161 regmap_update_bits(espi_ctrl->map, ESPI_INT_EN,
162 ESPI_INT_EN_HW_RST_DEASSERT,
163 ESPI_INT_EN_HW_RST_DEASSERT);
164
165 dev_set_drvdata(dev, espi_ctrl);
166
167 dev_info(dev, "module loaded\n");
168
169 return 0;
170 }
171
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Hi Chia-Wei,
I love your patch! Yet something to improve:
[auto build test ERROR on robh/for-next]
[also build test ERROR on arm/for-next keystone/next soc/for-next rockchip/for-next arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc7 next-20210826]
[cannot apply to xlnx/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arm64-randconfig-r002-20210826 (attached as .config)
compiler: aarch64-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/2980a1777c50754fe145f2e73ded8739931c0712
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737
git checkout 2980a1777c50754fe145f2e73ded8739931c0712
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arm64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:22:
drivers/soc/aspeed/aspeed-espi-perif.h: In function 'aspeed_espi_perif_alloc':
>> drivers/soc/aspeed/aspeed-espi-perif.h:446:43: error: passing argument 3 of 'of_property_read_u32' from incompatible pointer type [-Werror=incompatible-pointer-types]
446 | &espi_perif->mcyc_saddr);
| ^~~~~~~~~~~~~~~~~~~~~~~
| |
| phys_addr_t * {aka long long unsigned int *}
In file included from include/linux/of_device.h:9,
from drivers/soc/aspeed/aspeed-espi-ctrl.c:9:
include/linux/of.h:1249:45: note: expected 'u32 *' {aka 'unsigned int *'} but argument is of type 'phys_addr_t *' {aka 'long long unsigned int *'}
1249 | u32 *out_value)
| ~~~~~^~~~~~~~~
drivers/soc/aspeed/aspeed-espi-ctrl.c: In function 'aspeed_espi_ctrl_probe':
drivers/soc/aspeed/aspeed-espi-ctrl.c:98:30: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
98 | espi_ctrl->version = (uint32_t)of_device_get_match_data(dev);
| ^
cc1: some warnings being treated as errors
vim +/of_property_read_u32 +446 drivers/soc/aspeed/aspeed-espi-perif.h
422
423 static void *aspeed_espi_perif_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl)
424 {
425 int rc;
426 struct aspeed_espi_perif *espi_perif;
427 struct aspeed_espi_perif_dma *dma;
428
429 espi_perif = devm_kzalloc(dev, sizeof(*espi_perif), GFP_KERNEL);
430 if (!espi_perif)
431 return ERR_PTR(-ENOMEM);
432
433 espi_perif->ctrl = espi_ctrl;
434
435 init_waitqueue_head(&espi_perif->wq);
436
437 spin_lock_init(&espi_perif->lock);
438
439 mutex_init(&espi_perif->pc_rx_mtx);
440 mutex_init(&espi_perif->pc_tx_mtx);
441 mutex_init(&espi_perif->np_tx_mtx);
442
443 espi_perif->mcyc_enable = of_property_read_bool(dev->of_node, "perif,memcyc-enable");
444 if (espi_perif->mcyc_enable) {
445 rc = of_property_read_u32(dev->of_node, "perif,memcyc-src-addr",
> 446 &espi_perif->mcyc_saddr);
447 if (rc) {
448 dev_err(dev, "cannot get Host source address for memory cycle\n");
449 return ERR_PTR(-ENODEV);
450 }
451
452 rc = of_property_read_u32(dev->of_node, "perif,memcyc-size",
453 &espi_perif->mcyc_size);
454 if (rc) {
455 dev_err(dev, "cannot get size for memory cycle\n");
456 return ERR_PTR(-ENODEV);
457 }
458
459 if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN)
460 espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN;
461 else
462 espi_perif->mcyc_size = roundup_pow_of_two(espi_perif->mcyc_size);
463
464 espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1);
465 espi_perif->mcyc_virt = dma_alloc_coherent(dev, espi_perif->mcyc_size,
466 &espi_perif->mcyc_taddr, GFP_KERNEL);
467 if (!espi_perif->mcyc_virt) {
468 dev_err(dev, "cannot allocate memory cycle region\n");
469 return ERR_PTR(-ENOMEM);
470 }
471 }
472
473 if (of_property_read_bool(dev->of_node, "perif,dma-mode")) {
474 dma = &espi_perif->dma;
475
476 dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
477 &dma->pc_tx_addr, GFP_KERNEL);
478 if (!dma->pc_tx_virt) {
479 dev_err(dev, "cannot allocate posted TX DMA buffer\n");
480 return ERR_PTR(-ENOMEM);
481 }
482
483 dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
484 &dma->pc_rx_addr, GFP_KERNEL);
485 if (!dma->pc_rx_virt) {
486 dev_err(dev, "cannot allocate posted RX DMA buffer\n");
487 return ERR_PTR(-ENOMEM);
488 }
489
490 dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
491 &dma->np_tx_addr, GFP_KERNEL);
492 if (!dma->np_tx_virt) {
493 dev_err(dev, "cannot allocate non-posted TX DMA buffer\n");
494 return ERR_PTR(-ENOMEM);
495 }
496
497 espi_perif->dma_mode = 1;
498 }
499
500 espi_perif->mdev.parent = dev;
501 espi_perif->mdev.minor = MISC_DYNAMIC_MINOR;
502 espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", PERIF_MDEV_NAME);
503 espi_perif->mdev.fops = &aspeed_espi_perif_fops;
504 rc = misc_register(&espi_perif->mdev);
505 if (rc) {
506 dev_err(dev, "cannot register device\n");
507 return ERR_PTR(rc);
508 }
509
510 aspeed_espi_perif_enable(espi_perif);
511
512 return espi_perif;
513 }
514
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
> From: Rob Herring <[email protected]>
> Sent: Thursday, August 26, 2021 9:26 PM
>
> On Thu, 26 Aug 2021 14:16:20 +0800, Chia-Wei Wang wrote:
> > Add dt-bindings for Aspeed eSPI controller
> >
> > Signed-off-by: Chia-Wei Wang <[email protected]>
> > ---
> > .../devicetree/bindings/soc/aspeed/espi.yaml | 157
> > ++++++++++++++++++
> > 1 file changed, 157 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/soc/aspeed/espi.yaml
> >
>
> My bot found errors running 'make DT_CHECKER_FLAGS=-m
> dt_binding_check'
> on your patch (DT_CHECKER_FLAGS is new in v5.13):
>
> yamllint warnings/errors:
>
> dtschema/dtc warnings/errors:
> /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/as
> peed/espi.example.dt.yaml: espi@1e6ee000: 'espi-ctrl@0', 'espi-mmbi@800'
> do not match any of the regexes: 'pinctrl-[0-9]+'
> From schema:
> /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/as
> peed/espi.yaml
>
> doc reference errors (make refcheckdocs):
>
> See https://patchwork.ozlabs.org/patch/1520968
>
> This check can fail if there are any dependencies. The base for a patch series is
> generally the most recent rc1.
>
> If you already ran 'make dt_binding_check' and didn't see the above error(s),
> then make sure 'yamllint' is installed and dt-schema is up to
> date:
>
> pip3 install dtschema --upgrade
>
> Please check and re-submit.
The warning has been fixed by using patternProperties to define the espi-ctrl and the espi-mmbi child nodes.
The updated version will be submitted soon. Thanks.
Chiawei
Hi Chia-Wei,
[apologies for the re-send, dropping HTML part...]
> 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.
Great to have this added submitted upstream! A few comments though:
> ---
> drivers/soc/aspeed/Kconfig | 11 +
> drivers/soc/aspeed/Makefile | 1 +
> drivers/soc/aspeed/aspeed-espi-ctrl.c | 205 +++++++++
> drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 ++++++++++++
> drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++
> drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++++
> drivers/soc/aspeed/aspeed-espi-oob.h | 611 +++++++++++++++++++++++++
> drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++
> drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++++
This structure is a bit odd - you have the one -crtl.c file, which
defines the actual driver, but then a bunch of headers that contain more
code than header-type definitions.
Is there any reason that -flash, -ioc, -oob, -perif and -vw components
can't be standard .c files?
Then, for the userspace ABI: it looks like you're exposing everything
through new device-specific ioctls. Would it not make more sense to use
existing interfaces? For example, the virtual wire bits could be regular
GPIOs; the flash interface could be a mtd or block device.
I understand that we'll likely still need some level of custom device
control, but the more we can use generic interfaces for, the less custom
code (and interfaces) we'll need on the userspace side.
Cheers,
Jeremy
Hi Jeremy,
Thanks for reviewing the patch.
> From: Jeremy Kerr <[email protected]>
> Sent: Friday, August 27, 2021 11:20 AM
>
> Hi Chia-Wei,
>
> [apologies for the re-send, dropping HTML part...]
>
> > 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.
>
> Great to have this added submitted upstream! A few comments though:
>
> > ---
> > drivers/soc/aspeed/Kconfig | 11 +
> > drivers/soc/aspeed/Makefile | 1 +
> > drivers/soc/aspeed/aspeed-espi-ctrl.c | 205 +++++++++
> > drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 ++++++++++++
> > drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++
> > drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++++
> > drivers/soc/aspeed/aspeed-espi-oob.h | 611
> > +++++++++++++++++++++++++
> > drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++
> > drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++++
>
> This structure is a bit odd - you have the one -crtl.c file, which defines the
> actual driver, but then a bunch of headers that contain more code than
> header-type definitions.
>
> Is there any reason that -flash, -ioc, -oob, -perif and -vw components can't be
> standard .c files?
The eSPI slave device comprises four channels, where each of them has individual functionality.
Putting the four channels driver code into a single file makes it hard to maintain and trace.
We did consider to make them standard .c files.
But it requires to export channel functions into kernel space although they are dedicated only to this eSPI driver.
As espi-ctrl needs to invoke corresponding channel functions when it is interrupted by eSPI events.
To avoid polluting kernel space, we decided to put driver code in header files and make the channel functions 'static'.
BTW, I once encountered .c file inclusion in other projects. Is it proper for Linux driver development?
>
> Then, for the userspace ABI: it looks like you're exposing everything through
> new device-specific ioctls. Would it not make more sense to use existing
> interfaces? For example, the virtual wire bits could be regular GPIOs; the flash
> interface could be a mtd or block device.
>
> I understand that we'll likely still need some level of custom device control, but
> the more we can use generic interfaces for, the less custom code (and
> interfaces) we'll need on the userspace side.
>
eSPI communication is based on the its cycle packet format.
We intended to let userspace decided how to interpret and compose TX/RX packets including header, tag, length (encoded), and data.
IOCTL comes to our first mind as it also works in the 'packet' like paradigm.
Chiawei
Aspeed 5th and 6th generation SoCs are based on the ARM 32-bits architecture.
Should we follow the report to make the driver 64-bits compatible?
Or revise the driver to use more specific data types?
Thanks.
Chiawei
> From: kernel test robot <[email protected]>
> Sent: Friday, August 27, 2021 7:30 AM
>
> Hi Chia-Wei,
>
> I love your patch! Yet something to improve:
>
> [auto build test ERROR on robh/for-next] [also build test ERROR on
> arm/for-next keystone/next soc/for-next rockchip/for-next
> arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc7
> next-20210826] [cannot apply to xlnx/master] [If your patch is applied to the
> wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch]
>
> url:
> https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-e
> SPI-support/20210826-141737
> base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
> config: arm64-randconfig-r002-20210826 (attached as .config)
> compiler: aarch64-linux-gcc (GCC) 11.2.0 reproduce (this is a W=1 build):
> wget
> https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O
> ~/bin/make.cross
> chmod +x ~/bin/make.cross
> #
> https://github.com/0day-ci/linux/commit/2980a1777c50754fe145f2e73ded873
> 9931c0712
> git remote add linux-review https://github.com/0day-ci/linux
> git fetch --no-tags linux-review
> Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737
> git checkout 2980a1777c50754fe145f2e73ded8739931c0712
> # save the attached .config to linux build tree
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0
> make.cross ARCH=arm64
>
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <[email protected]>
>
> All errors (new ones prefixed by >>):
>
> In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:22:
> drivers/soc/aspeed/aspeed-espi-perif.h: In function
> 'aspeed_espi_perif_alloc':
> >> drivers/soc/aspeed/aspeed-espi-perif.h:446:43: error: passing
> >> argument 3 of 'of_property_read_u32' from incompatible pointer type
> >> [-Werror=incompatible-pointer-types]
> 446 |
> &espi_perif->mcyc_saddr);
> |
> ^~~~~~~~~~~~~~~~~~~~~~~
> | |
> | phys_addr_t
> * {aka long long unsigned int *}
> In file included from include/linux/of_device.h:9,
> from drivers/soc/aspeed/aspeed-espi-ctrl.c:9:
> include/linux/of.h:1249:45: note: expected 'u32 *' {aka 'unsigned int *'} but
> argument is of type 'phys_addr_t *' {aka 'long long unsigned int *'}
> 1249 | u32 *out_value)
> | ~~~~~^~~~~~~~~
> drivers/soc/aspeed/aspeed-espi-ctrl.c: In function
> 'aspeed_espi_ctrl_probe':
> drivers/soc/aspeed/aspeed-espi-ctrl.c:98:30: warning: cast from pointer to
> integer of different size [-Wpointer-to-int-cast]
> 98 | espi_ctrl->version =
> (uint32_t)of_device_get_match_data(dev);
> | ^
> cc1: some warnings being treated as errors
>
>
> vim +/of_property_read_u32 +446 drivers/soc/aspeed/aspeed-espi-perif.h
>
> 422
> 423 static void *aspeed_espi_perif_alloc(struct device *dev, struct
> aspeed_espi_ctrl *espi_ctrl)
> 424 {
> 425 int rc;
> 426 struct aspeed_espi_perif *espi_perif;
> 427 struct aspeed_espi_perif_dma *dma;
> 428
> 429 espi_perif = devm_kzalloc(dev, sizeof(*espi_perif),
> GFP_KERNEL);
> 430 if (!espi_perif)
> 431 return ERR_PTR(-ENOMEM);
> 432
> 433 espi_perif->ctrl = espi_ctrl;
> 434
> 435 init_waitqueue_head(&espi_perif->wq);
> 436
> 437 spin_lock_init(&espi_perif->lock);
> 438
> 439 mutex_init(&espi_perif->pc_rx_mtx);
> 440 mutex_init(&espi_perif->pc_tx_mtx);
> 441 mutex_init(&espi_perif->np_tx_mtx);
> 442
> 443 espi_perif->mcyc_enable =
> of_property_read_bool(dev->of_node, "perif,memcyc-enable");
> 444 if (espi_perif->mcyc_enable) {
> 445 rc = of_property_read_u32(dev->of_node,
> "perif,memcyc-src-addr",
> > 446 &espi_perif->mcyc_saddr);
> 447 if (rc) {
> 448 dev_err(dev, "cannot get Host source address for
> memory cycle\n");
> 449 return ERR_PTR(-ENODEV);
> 450 }
> 451
> 452 rc = of_property_read_u32(dev->of_node,
> "perif,memcyc-size",
> 453 &espi_perif->mcyc_size);
> 454 if (rc) {
> 455 dev_err(dev, "cannot get size for memory cycle\n");
> 456 return ERR_PTR(-ENODEV);
> 457 }
> 458
> 459 if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN)
> 460 espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN;
> 461 else
> 462 espi_perif->mcyc_size =
> roundup_pow_of_two(espi_perif->mcyc_size);
> 463
> 464 espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1);
> 465 espi_perif->mcyc_virt = dma_alloc_coherent(dev,
> espi_perif->mcyc_size,
> 466 &espi_perif->mcyc_taddr,
> GFP_KERNEL);
> 467 if (!espi_perif->mcyc_virt) {
> 468 dev_err(dev, "cannot allocate memory cycle
> region\n");
> 469 return ERR_PTR(-ENOMEM);
> 470 }
> 471 }
> 472
> 473 if (of_property_read_bool(dev->of_node, "perif,dma-mode")) {
> 474 dma = &espi_perif->dma;
> 475
> 476 dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> 477 &dma->pc_tx_addr,
> GFP_KERNEL);
> 478 if (!dma->pc_tx_virt) {
> 479 dev_err(dev, "cannot allocate posted TX DMA
> buffer\n");
> 480 return ERR_PTR(-ENOMEM);
> 481 }
> 482
> 483 dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> 484 &dma->pc_rx_addr,
> GFP_KERNEL);
> 485 if (!dma->pc_rx_virt) {
> 486 dev_err(dev, "cannot allocate posted RX DMA
> buffer\n");
> 487 return ERR_PTR(-ENOMEM);
> 488 }
> 489
> 490 dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
> 491 &dma->np_tx_addr, GFP_KERNEL);
> 492 if (!dma->np_tx_virt) {
> 493 dev_err(dev, "cannot allocate non-posted TX DMA
> buffer\n");
> 494 return ERR_PTR(-ENOMEM);
> 495 }
> 496
> 497 espi_perif->dma_mode = 1;
> 498 }
> 499
> 500 espi_perif->mdev.parent = dev;
> 501 espi_perif->mdev.minor = MISC_DYNAMIC_MINOR;
> 502 espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL,
> "%s", PERIF_MDEV_NAME);
> 503 espi_perif->mdev.fops = &aspeed_espi_perif_fops;
> 504 rc = misc_register(&espi_perif->mdev);
> 505 if (rc) {
> 506 dev_err(dev, "cannot register device\n");
> 507 return ERR_PTR(rc);
> 508 }
> 509
> 510 aspeed_espi_perif_enable(espi_perif);
> 511
> 512 return espi_perif;
> 513 }
> 514
>
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/[email protected]
Hi Chiawei,
> The eSPI slave device comprises four channels, where each of them has
> individual functionality. Putting the four channels driver code into a
> single file makes it hard to maintain and trace.
Yep, understood.
> We did consider to make them standard .c files.
> But it requires to export channel functions into kernel space
> although they are dedicated only to this eSPI driver.
What do you mean by "export into kernel space" here? The function
prototypes need to be available to your main (-ctrl.c) file, regardless
of whether you're putting the entire functions in a header file, or just
the prototype. There's doesn't need to be any difference in visibility
outside of your own module if you were to do this the usual way.
> As espi-ctrl needs to invoke corresponding channel functions when it
> is interrupted by eSPI events.
>
> To avoid polluting kernel space, we decided to put driver code in
> header files and make the channel functions 'static'.
>
> BTW, I once encountered .c file inclusion in other projects. Is it
> proper for Linux driver development?
It can be, just that in this case it's a bit unusual, and I can't see a
good reason for doing so. This could just be a standard
multiple-source-file module.
> eSPI communication is based on the its cycle packet format.
> We intended to let userspace decided how to interpret and compose
> TX/RX packets including header, tag, length (encoded), and data.
> IOCTL comes to our first mind as it also works in the 'packet' like
> paradigm.
But you're not always exposing a packet-like interface for this. For
example, your virtual-wire interface just has a get/set interface for
bits in a register (plus some PCH event handling, which may not be
applicable to all platforms...).
The other channels do look like more of a packet interface though, but
in that case I'm not convinced that an ioctl interface is the best way
to go for that. You're essentially sending a (length, pointer) pair
over the ioctls there, which sounds more like a write() than an ioctl().
Regardless of the choice of interface though, this will definitely need
some documentation or description of the API, and the ioc header to be
somewhere useful for userspace to consume.
With that documented, we'd have a better idea of how the new ABI is
supposed to work.
Cheers,
Jeremy
On Fri, 27 Aug 2021 at 03:52, ChiaWei Wang <[email protected]> wrote:
>
> Aspeed 5th and 6th generation SoCs are based on the ARM 32-bits architecture.
> Should we follow the report to make the driver 64-bits compatible?
> Or revise the driver to use more specific data types?
Yes, in general it's expected your driver will compile cleanly for
64-bit architectures. This helps with testing and static analysis,
where CI builds all the drivers for x86.
Cheers,
Joel
Hi Jeremy
> From: Jeremy Kerr <[email protected]>
> Sent: Friday, August 27, 2021 12:37 PM
>
> Hi Chiawei,
>
> > The eSPI slave device comprises four channels, where each of them has
> > individual functionality. Putting the four channels driver code into
> > a single file makes it hard to maintain and trace.
>
> Yep, understood.
>
> > We did consider to make them standard .c files.
> > But it requires to export channel functions into kernel space although
> > they are dedicated only to this eSPI driver.
>
> What do you mean by "export into kernel space" here? The function prototypes
The channel functions will be visible to all kernel driver files.
> need to be available to your main (-ctrl.c) file, regardless of whether you're
> putting the entire functions in a header file, or just the prototype. There's
> doesn't need to be any difference in visibility outside of your own module if
> you were to do this the usual way.
Maybe I was trying to make channels function visible only to espi-ctrl.c too far.
I will revise the driver to present in the usual .c way.
>
> > As espi-ctrl needs to invoke corresponding channel functions when it
> > is interrupted by eSPI events.
> >
> > To avoid polluting kernel space, we decided to put driver code in
> > header files and make the channel functions 'static'.
> >
> > BTW, I once encountered .c file inclusion in other projects. Is it
> > proper for Linux driver development?
>
> It can be, just that in this case it's a bit unusual, and I can't see a good reason
> for doing so. This could just be a standard multiple-source-file module.
>
> > eSPI communication is based on the its cycle packet format.
> > We intended to let userspace decided how to interpret and compose
> > TX/RX packets including header, tag, length (encoded), and data.
> > IOCTL comes to our first mind as it also works in the 'packet' like
> > paradigm.
>
> But you're not always exposing a packet-like interface for this. For example,
> your virtual-wire interface just has a get/set interface for bits in a register
> (plus some PCH event handling, which may not be applicable to all
> platforms...).
>
> The other channels do look like more of a packet interface though, but in that
> case I'm not convinced that an ioctl interface is the best way to go for that.
> You're essentially sending a (length, pointer) pair over the ioctls there, which
> sounds more like a write() than an ioctl().
In most cases, yes.
Currently only the peripheral channel has more than the 2 (put tx/get rx) IOCTL code.
We think it might be a good idea to make the user interfaces of all channels consistent using IOCTL.
>
> Regardless of the choice of interface though, this will definitely need some
> documentation or description of the API, and the ioc header to be somewhere
> useful for userspace to consume.
>
> With that documented, we'd have a better idea of how the new ABI is
> supposed to work.
Sure. more comments will be added in aspeed-espi-ioc.h to describe the usage and the purpose.
Thanks for your feedback.
Chiawei
> From: Joel Stanley <[email protected]>
> Sent: Friday, August 27, 2021 1:49 PM
>
> On Fri, 27 Aug 2021 at 03:52, ChiaWei Wang
> <[email protected]> wrote:
> >
> > Aspeed 5th and 6th generation SoCs are based on the ARM 32-bits
> architecture.
> > Should we follow the report to make the driver 64-bits compatible?
> > Or revise the driver to use more specific data types?
>
> Yes, in general it's expected your driver will compile cleanly for 64-bit
> architectures. This helps with testing and static analysis, where CI builds all the
> drivers for x86.
Understood. Will fix the data type issue in the next submission.
Thanks.
Chiawei