linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] arm: aspeed: Add eSPI support
@ 2021-01-06  5:59 Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller Chia-Wei, Wang
                   ` (5 more replies)
  0 siblings, 6 replies; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

This patch series add the driver support for the eSPI controller
of Aspeed 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.

Chia-Wei, Wang (6):
  dt-bindings: aspeed: Add eSPI controller
  MAINTAINER: Add ASPEED eSPI driver entry
  clk: ast2600: Add eSPI reset bit
  irqchip/aspeed: Add Aspeed eSPI interrupt controller
  soc: aspeed: Add eSPI driver
  ARM: dts: aspeed: Add AST2600 eSPI nodes

 .../devicetree/bindings/soc/aspeed/espi.yaml  | 252 +++++++
 MAINTAINERS                                   |  14 +
 arch/arm/boot/dts/aspeed-g6.dtsi              |  57 ++
 drivers/irqchip/Makefile                      |   2 +-
 drivers/irqchip/irq-aspeed-espi-ic.c          | 251 +++++++
 drivers/soc/aspeed/Kconfig                    |  49 ++
 drivers/soc/aspeed/Makefile                   |   5 +
 drivers/soc/aspeed/aspeed-espi-ctrl.c         | 197 +++++
 drivers/soc/aspeed/aspeed-espi-flash.c        | 490 ++++++++++++
 drivers/soc/aspeed/aspeed-espi-oob.c          | 706 ++++++++++++++++++
 drivers/soc/aspeed/aspeed-espi-peripheral.c   | 613 +++++++++++++++
 drivers/soc/aspeed/aspeed-espi-vw.c           | 211 ++++++
 include/dt-bindings/clock/ast2600-clock.h     |   1 +
 .../interrupt-controller/aspeed-espi-ic.h     |  15 +
 include/soc/aspeed/espi.h                     | 279 +++++++
 include/uapi/linux/aspeed-espi.h              | 160 ++++
 16 files changed, 3301 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml
 create mode 100644 drivers/irqchip/irq-aspeed-espi-ic.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-peripheral.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c
 create mode 100644 include/dt-bindings/interrupt-controller/aspeed-espi-ic.h
 create mode 100644 include/soc/aspeed/espi.h
 create mode 100644 include/uapi/linux/aspeed-espi.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller
  2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
@ 2021-01-06  5:59 ` Chia-Wei, Wang
  2021-01-06 15:07   ` Rob Herring
  2021-01-06  5:59 ` [PATCH 2/6] MAINTAINER: Add ASPEED eSPI driver entry Chia-Wei, Wang
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

Add dt-bindings and the inclusion header for Aspeed eSPI controller.

Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
---
 .../devicetree/bindings/soc/aspeed/espi.yaml  | 252 ++++++++++++++++++
 .../interrupt-controller/aspeed-espi-ic.h     |  15 ++
 2 files changed, 267 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml
 create mode 100644 include/dt-bindings/interrupt-controller/aspeed-espi-ic.h

diff --git a/Documentation/devicetree/bindings/soc/aspeed/espi.yaml b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml
new file mode 100644
index 000000000000..ee92b66fe15b
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml
@@ -0,0 +1,252 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# # Copyright (c) 2020 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 <chiawei_wang@aspeedtech.com>
+  - Ryan Chen <ryan_chen@aspeedtech.com>
+
+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,ast2600-espi
+      - const: simple-mfd
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 1
+
+  ranges: true
+
+  espi-ic:
+    type: object
+
+    properties:
+      compatible:
+        const: aspeed,ast2600-espi-ic
+
+      interrupts:
+        maxItems: 2
+
+      interrupt-controller: true
+
+      "#interrupt-cells":
+        const: 1
+        description:
+          The cell is a SW-encoded number for IRQ dispatching to eSPI channels
+
+    required:
+      - compatible
+      - interrupts
+      - interrupt-controller
+      - "#interrupt-cells"
+
+  espi-ctrl:
+    type: object
+
+    properties:
+      compatible:
+        const: aspeed,ast2600-espi-ctrl
+
+      interrupts:
+        maxItems: 2
+
+      clocks:
+        maxItems: 1
+
+      resets:
+        maxItems: 1
+
+    required:
+      - compatible
+      - interrupts
+      - clocks
+      - resets
+
+  espi-peripheral-channel:
+    type: object
+
+    properties:
+      compatible:
+        const: aspeed,ast2600-espi-peripheral
+
+      interrupts:
+        maxItems: 2
+
+      dma-mode:
+        type: boolean
+
+      memcyc,map-src-addr:
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        description: The host side address to be decoded into the memory cycle over eSPI peripheral channel
+
+      memcyc,map-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
+
+    required:
+      - compatible
+      - interrupts
+      - memcyc,map-src-addr
+      - memcyc,map-size
+
+  espi-vw-channel:
+    type: object
+
+    properties:
+      compatible:
+        const: aspeed,ast2600-espi-vw
+
+      interrupts:
+        maxItems: 2
+
+    required:
+      - compatible
+      - interrupts
+
+  espi-oob-channel:
+    type: object
+
+    properties:
+      compatible:
+        const: aspeed,ast2600-espi-oob
+
+      interrupts:
+        maxItems: 2
+
+      dma-mode:
+        type: boolean
+
+      dma-tx-desc-num:
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 2
+        maximum: 1023
+
+      dma-rx-desc-num:
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 2
+        maximum: 1023
+
+    required:
+      - compatible
+      - interrupts
+
+  espi-flash-channel:
+    type: object
+
+    properties:
+      compatible:
+        const: aspeed,ast2600-espi-flash
+
+      interrupts:
+        maxItems: 2
+
+      dma-mode:
+        type: boolean
+
+      safs-mode:
+        description: Slave-Attached-Sharing-Flash mode, 0->Mix, 1->SW, 2->HW
+        enum: [ 0, 1, 2 ]
+
+    required:
+      - compatible
+      - interrupts
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+  - espi-ic
+  - espi-ctrl
+  - espi-peripheral-channel
+  - espi-vw-channel
+  - espi-oob-channel
+  - espi-flash-channel
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    #include <dt-bindings/gpio/aspeed-gpio.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_ic: espi-ic {
+            #interrupt-cells = <1>;
+            compatible = "aspeed,ast2600-espi-ic";
+            interrupts-extended = <&gic GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
+                                  <&gpio0 ASPEED_GPIO(W, 7) IRQ_TYPE_EDGE_FALLING>;
+            interrupt-controller;
+        };
+
+        espi_ctrl: espi-ctrl {
+            compatible = "aspeed,ast2600-espi-ctrl";
+            interrupts-extended = <&espi_ic ASPEED_ESPI_IC_CTRL_EVENT>,
+                                  <&espi_ic ASPEED_ESPI_IC_CTRL_RESET>;
+            clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
+            resets = <&syscon ASPEED_RESET_ESPI>;
+        };
+
+        espi_peripheral: espi-peripheral-channel {
+            compatible = "aspeed,ast2600-espi-peripheral";
+            interrupts-extended = <&espi_ic ASPEED_ESPI_IC_PERIF_EVENT>,
+                                  <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+            dma-mode;
+            memcyc,map-src-addr = <0x98000000>;
+            memcyc,map-size = <0x10000>;
+        };
+
+        espi_vw: espi-vw-channel {
+            compatible = "aspeed,ast2600-espi-vw";
+            interrupts-extended = <&espi_ic ASPEED_ESPI_IC_VW_EVENT>,
+                                  <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+        };
+
+        espi_oob: espi-oob-channel {
+            compatible = "aspeed,ast2600-espi-oob";
+            interrupts-extended = <&espi_ic ASPEED_ESPI_IC_OOB_EVENT>,
+                                  <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+            dma-mode;
+            dma-tx-desc-num = <2>;
+            dma-rx-desc-num = <2>;
+        };
+
+        espi_flash: espi-flash-channel {
+            compatible = "aspeed,ast2600-espi-flash";
+            interrupts-extended = <&espi_ic ASPEED_ESPI_IC_FLASH_EVENT>,
+                                  <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+            dma-mode;
+            safs-mode = <2>;
+        };
+    };
+
+...
diff --git a/include/dt-bindings/interrupt-controller/aspeed-espi-ic.h b/include/dt-bindings/interrupt-controller/aspeed-espi-ic.h
new file mode 100644
index 000000000000..85faa198350d
--- /dev/null
+++ b/include/dt-bindings/interrupt-controller/aspeed-espi-ic.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_ASPEED_ESPI_IC_H_
+#define _DT_BINDINGS_INTERRUPT_CONTROLLER_ASPEED_ESPI_IC_H_
+
+/* SW-decoded IRQs for eSPI host HW reset and channel events */
+#define ASPEED_ESPI_IC_CTRL_RESET	0
+#define ASPEED_ESPI_IC_CHAN_RESET	1
+#define ASPEED_ESPI_IC_CTRL_EVENT	2
+#define ASPEED_ESPI_IC_PERIF_EVENT	3
+#define ASPEED_ESPI_IC_VW_EVENT		4
+#define ASPEED_ESPI_IC_OOB_EVENT	5
+#define ASPEED_ESPI_IC_FLASH_EVENT	6
+
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/6] MAINTAINER: Add ASPEED eSPI driver entry
  2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller Chia-Wei, Wang
@ 2021-01-06  5:59 ` Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 3/6] clk: ast2600: Add eSPI reset bit Chia-Wei, Wang
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

Add myself and Ryan Chen as maintainer of the Aspeed eSPI
driver and the associated eSPI interrupt controller.
Joel Stanley is also added as the reviewer.

Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
---
 MAINTAINERS | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 7c1e45c416b1..d5f9205a5439 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1639,6 +1639,20 @@ F:	drivers/crypto/axis
 F:	drivers/mmc/host/usdhi6rol0.c
 F:	drivers/pinctrl/pinctrl-artpec*
 
+ARM/ASPEED ESPI DRIVER
+M:	Chia-Wei Wang <chiawei_wang@aspeedtech.com>
+M:	Ryan Chen <ryan_chen@aspeedtech.com>
+R:	Joel Stanley <joel@jms.id.au>
+L:	linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
+L:	openbmc@lists.ozlabs.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/soc/aspeed/espi.yaml
+F:	drivers/irqchip/irq-aspeed-espi-ic.c
+F:	drivers/soc/aspeed/aspeed-espi*
+F:	include/dt-bindings/interrupt-controller/aspeed-espi-ic.h
+F:	include/soc/aspeed/espi.h
+F:	include/uapi/linux/aspeed-espi.h
+
 ARM/ASPEED I2C DRIVER
 M:	Brendan Higgins <brendanhiggins@google.com>
 R:	Benjamin Herrenschmidt <benh@kernel.crashing.org>
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 3/6] clk: ast2600: Add eSPI reset bit
  2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 2/6] MAINTAINER: Add ASPEED eSPI driver entry Chia-Wei, Wang
@ 2021-01-06  5:59 ` Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller Chia-Wei, Wang
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

Add bit field definition for the eSPI reset control.

Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
---
 include/dt-bindings/clock/ast2600-clock.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h
index 62b9520a00fd..964934b1caef 100644
--- a/include/dt-bindings/clock/ast2600-clock.h
+++ b/include/dt-bindings/clock/ast2600-clock.h
@@ -89,6 +89,7 @@
 #define ASPEED_CLK_MAC4RCLK		70
 
 /* Only list resets here that are not part of a gate */
+#define ASPEED_RESET_ESPI		57
 #define ASPEED_RESET_ADC		55
 #define ASPEED_RESET_JTAG_MASTER2	54
 #define ASPEED_RESET_I3C_DMA		39
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
  2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
                   ` (2 preceding siblings ...)
  2021-01-06  5:59 ` [PATCH 3/6] clk: ast2600: Add eSPI reset bit Chia-Wei, Wang
@ 2021-01-06  5:59 ` Chia-Wei, Wang
  2021-01-06 10:59   ` Marc Zyngier
  2021-01-06  5:59 ` [PATCH 5/6] soc: aspeed: Add eSPI driver Chia-Wei, Wang
  2021-01-06  5:59 ` [PATCH 6/6] ARM: dts: aspeed: Add AST2600 eSPI nodes Chia-Wei, Wang
  5 siblings, 1 reply; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

The eSPI interrupt controller acts as a SW IRQ number
decoder to correctly control/dispatch interrupts of
the eSPI peripheral, virtual wire, out-of-band, and
flash channels.

Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
---
 drivers/irqchip/Makefile             |   2 +-
 drivers/irqchip/irq-aspeed-espi-ic.c | 251 ++++++++++++++++++++++++
 include/soc/aspeed/espi.h            | 279 +++++++++++++++++++++++++++
 3 files changed, 531 insertions(+), 1 deletion(-)
 create mode 100644 drivers/irqchip/irq-aspeed-espi-ic.c
 create mode 100644 include/soc/aspeed/espi.h

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 0ac93bfaec61..56da4a3123f8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -86,7 +86,7 @@ obj-$(CONFIG_MVEBU_PIC)			+= irq-mvebu-pic.o
 obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
 obj-$(CONFIG_LS_EXTIRQ)			+= irq-ls-extirq.o
 obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
-obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o
+obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o irq-aspeed-espi-ic.o
 obj-$(CONFIG_STM32_EXTI) 		+= irq-stm32-exti.o
 obj-$(CONFIG_QCOM_IRQ_COMBINER)		+= qcom-irq-combiner.o
 obj-$(CONFIG_IRQ_UNIPHIER_AIDET)	+= irq-uniphier-aidet.o
diff --git a/drivers/irqchip/irq-aspeed-espi-ic.c b/drivers/irqchip/irq-aspeed-espi-ic.c
new file mode 100644
index 000000000000..8a5cc8fe3f0c
--- /dev/null
+++ b/drivers/irqchip/irq-aspeed-espi-ic.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ */
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <soc/aspeed/espi.h>
+#include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
+
+#define DEVICE_NAME	"aspeed-espi-ic"
+#define IRQCHIP_NAME	"eSPI-IC"
+
+#define ESPI_IC_IRQ_NUM	7
+
+struct aspeed_espi_ic {
+	struct regmap *map;
+	int irq;
+	int gpio_irq;
+	struct irq_domain *irq_domain;
+};
+
+static void aspeed_espi_ic_gpio_isr(struct irq_desc *desc)
+{
+	unsigned int irq;
+	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+
+	chained_irq_enter(chip, desc);
+
+	irq = irq_find_mapping(espi_ic->irq_domain,
+				   ASPEED_ESPI_IC_CTRL_RESET);
+	generic_handle_irq(irq);
+
+	irq = irq_find_mapping(espi_ic->irq_domain,
+				   ASPEED_ESPI_IC_CHAN_RESET);
+	generic_handle_irq(irq);
+
+	chained_irq_exit(chip, desc);
+}
+
+static void aspeed_espi_ic_isr(struct irq_desc *desc)
+{
+	unsigned int sts;
+	unsigned int irq;
+	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+
+	chained_irq_enter(chip, desc);
+
+	regmap_read(espi_ic->map, ESPI_INT_STS, &sts);
+
+	if (sts & ESPI_INT_STS_PERIF_BITS) {
+		irq = irq_find_mapping(espi_ic->irq_domain,
+				       ASPEED_ESPI_IC_PERIF_EVENT);
+		generic_handle_irq(irq);
+	}
+
+	if (sts & ESPI_INT_STS_VW_BITS) {
+		irq = irq_find_mapping(espi_ic->irq_domain,
+				       ASPEED_ESPI_IC_VW_EVENT);
+		generic_handle_irq(irq);
+	}
+
+	if (sts & ESPI_INT_STS_OOB_BITS) {
+		irq = irq_find_mapping(espi_ic->irq_domain,
+				       ASPEED_ESPI_IC_OOB_EVENT);
+		generic_handle_irq(irq);
+	}
+
+	if (sts & ESPI_INT_STS_FLASH_BITS) {
+		irq = irq_find_mapping(espi_ic->irq_domain,
+				       ASPEED_ESPI_IC_FLASH_EVENT);
+		generic_handle_irq(irq);
+	}
+
+	if (sts & ESPI_INT_STS_HW_RST_DEASSERT) {
+		irq = irq_find_mapping(espi_ic->irq_domain,
+				       ASPEED_ESPI_IC_CTRL_EVENT);
+		generic_handle_irq(irq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void aspeed_espi_ic_irq_disable(struct irq_data *data)
+{
+	struct aspeed_espi_ic *espi_ic = irq_data_get_irq_chip_data(data);
+
+	switch (data->hwirq) {
+	case ASPEED_ESPI_IC_CTRL_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_HW_RST_DEASSERT,
+				   0);
+		break;
+	case ASPEED_ESPI_IC_PERIF_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_PERIF_BITS, 0);
+		break;
+	case ASPEED_ESPI_IC_VW_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_VW_BITS, 0);
+		break;
+	case ASPEED_ESPI_IC_OOB_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_OOB_BITS, 0);
+		break;
+	case ASPEED_ESPI_IC_FLASH_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_FLASH_BITS, 0);
+		break;
+	}
+}
+
+static void aspeed_espi_ic_irq_enable(struct irq_data *data)
+{
+	struct aspeed_espi_ic *espi_ic = irq_data_get_irq_chip_data(data);
+
+	switch (data->hwirq) {
+	case ASPEED_ESPI_IC_CTRL_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_HW_RST_DEASSERT,
+				   ESPI_INT_EN_HW_RST_DEASSERT);
+		break;
+	case ASPEED_ESPI_IC_PERIF_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_PERIF_BITS,
+				   ESPI_INT_EN_PERIF_BITS);
+		break;
+	case ASPEED_ESPI_IC_VW_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_VW_BITS,
+				   ESPI_INT_EN_VW_BITS);
+		break;
+	case ASPEED_ESPI_IC_OOB_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_OOB_BITS,
+				   ESPI_INT_EN_OOB_BITS);
+		break;
+	case ASPEED_ESPI_IC_FLASH_EVENT:
+		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
+				   ESPI_INT_EN_FLASH_BITS,
+				   ESPI_INT_EN_FLASH_BITS);
+		break;
+	}
+}
+
+static struct irq_chip aspeed_espi_ic_chip = {
+	.name = IRQCHIP_NAME,
+	.irq_enable = aspeed_espi_ic_irq_enable,
+	.irq_disable = aspeed_espi_ic_irq_disable,
+};
+
+static int aspeed_espi_ic_map(struct irq_domain *domain, unsigned int irq,
+			     irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &aspeed_espi_ic_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops aspeed_espi_ic_domain_ops = {
+	.map = aspeed_espi_ic_map,
+};
+
+static int aspeed_espi_ic_probe(struct platform_device *pdev)
+{
+	struct device *dev;
+	struct aspeed_espi_ic *espi_ic;
+
+	dev = &pdev->dev;
+
+	espi_ic = devm_kzalloc(dev, sizeof(*espi_ic), GFP_KERNEL);
+	if (!espi_ic)
+		return -ENOMEM;
+
+	espi_ic->map = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(espi_ic->map)) {
+		dev_err(dev, "cannot get regmap\n");
+		return -ENODEV;
+	}
+
+	espi_ic->irq = platform_get_irq(pdev, 0);
+	if (espi_ic->irq < 0)
+		return espi_ic->irq;
+
+	espi_ic->gpio_irq = platform_get_irq(pdev, 1);
+	if (espi_ic->gpio_irq < 0)
+		return espi_ic->gpio_irq;
+
+	espi_ic->irq_domain = irq_domain_add_linear(dev->of_node, ESPI_IC_IRQ_NUM,
+						    &aspeed_espi_ic_domain_ops,
+						    espi_ic);
+	if (!espi_ic->irq_domain) {
+		dev_err(dev, "cannot to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	irq_set_chained_handler_and_data(espi_ic->irq,
+					 aspeed_espi_ic_isr,
+					 espi_ic);
+
+	irq_set_chained_handler_and_data(espi_ic->gpio_irq,
+					 aspeed_espi_ic_gpio_isr,
+					 espi_ic);
+
+	dev_set_drvdata(dev, espi_ic);
+
+	dev_info(dev, "eSPI IRQ controller initialized\n");
+
+	return 0;
+}
+
+static int aspeed_espi_ic_remove(struct platform_device *pdev)
+{
+	struct aspeed_espi_ic *espi_ic = platform_get_drvdata(pdev);
+
+	irq_domain_remove(espi_ic->irq_domain);
+	return 0;
+}
+
+static const struct of_device_id aspeed_espi_ic_of_matches[] = {
+	{ .compatible = "aspeed,ast2600-espi-ic" },
+	{ },
+};
+
+static struct platform_driver aspeed_espi_ic_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = aspeed_espi_ic_of_matches,
+	},
+	.probe = aspeed_espi_ic_probe,
+	.remove = aspeed_espi_ic_remove,
+};
+
+module_platform_driver(aspeed_espi_ic_driver);
+
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("Aspeed eSPI interrupt controller");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/aspeed/espi.h b/include/soc/aspeed/espi.h
new file mode 100644
index 000000000000..c9a4f51737ee
--- /dev/null
+++ b/include/soc/aspeed/espi.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ * Author: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
+ */
+#ifndef _ASPEED_ESPI_H_
+#define _ASPEED_ESPI_H_
+
+#include <linux/bits.h>
+
+/* eSPI register offset */
+#define ESPI_CTRL			0x000
+#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_INT_EN_SUSPEND_WARN	BIT(0)
+#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	BIT(8)
+#define   ESPI_SYSEVT_INT_T2_OOB_RST_WARN	BIT(6)
+#define ESPI_SYSEVT_INT_STS		0x11c
+#define   ESPI_SYSEVT_INT_STS_NMI_OUT		BIT(10)
+#define   ESPI_SYSEVT_INT_STS_SMI_OUT		BIT(9)
+#define   ESPI_SYSEVT_INT_STS_HOST_RST_WARN	BIT(8)
+#define   ESPI_SYSEVT_INT_STS_OOB_RST_WARN	BIT(6)
+#define   ESPI_SYSEVT_INT_STS_PLTRSTN		BIT(5)
+#define   ESPI_SYSEVT_INT_STS_SUSPEND		BIT(4)
+#define   ESPI_SYSEVT_INT_STS_S5_SLEEP		BIT(2)
+#define   ESPI_SYSEVT_INT_STS_S4_SLEEP		BIT(1)
+#define   ESPI_SYSEVT_INT_STS_S3_SLEEP		BIT(0)
+#define ESPI_SYSEVT1_INT_T0		0x120
+#define   ESPI_SYSEVT1_INT_T0_SUSPEND_WARN	BIT(0)
+#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	BIT(0)
+#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(25, 16)
+#define   ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT	16
+#define   ESPI_OOB_RX_DMA_WS_PTR_WP_MASK	GENMASK(9, 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
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 5/6] soc: aspeed: Add eSPI driver
  2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
                   ` (3 preceding siblings ...)
  2021-01-06  5:59 ` [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller Chia-Wei, Wang
@ 2021-01-06  5:59 ` Chia-Wei, Wang
  2021-01-06 15:32   ` Rob Herring
  2021-01-06  5:59 ` [PATCH 6/6] ARM: dts: aspeed: Add AST2600 eSPI nodes Chia-Wei, Wang
  5 siblings, 1 reply; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

The Aspeed eSPI controller is slave device to communicate with
the master through the Enhanced Serial Peripheral Interface (eSPI).
All of the four eSPI channels, namely peripheral, virtual wire,
out-of-band, and flash are supported.

Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
---
 drivers/soc/aspeed/Kconfig                  |  49 ++
 drivers/soc/aspeed/Makefile                 |   5 +
 drivers/soc/aspeed/aspeed-espi-ctrl.c       | 197 ++++++
 drivers/soc/aspeed/aspeed-espi-flash.c      | 490 ++++++++++++++
 drivers/soc/aspeed/aspeed-espi-oob.c        | 706 ++++++++++++++++++++
 drivers/soc/aspeed/aspeed-espi-peripheral.c | 613 +++++++++++++++++
 drivers/soc/aspeed/aspeed-espi-vw.c         | 211 ++++++
 include/uapi/linux/aspeed-espi.h            | 160 +++++
 8 files changed, 2431 insertions(+)
 create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-peripheral.c
 create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c
 create mode 100644 include/uapi/linux/aspeed-espi.h

diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index 243ca196e6ad..e4408e97023d 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -42,6 +42,55 @@ config ASPEED_SOCINFO
 	help
 	  Say yes to support decoding of ASPEED BMC information.
 
+config ASPEED_ESPI
+	tristate "Aspeed eSPI Engine Driver"
+	select REGMAP
+	select MFD_SYSON
+	select ASPEED_ESPI_PERIPHERAL
+	select ASPEED_ESPI_VW
+	select ASPEED_ESPI_OOB
+	select ASPEED_ESPI_FLASH
+	default n
+	help
+	  Enable support for the Aspeed eSPI engine. The eSPI engine
+	  plays as a slave device in BMC to communicate with the host
+	  side master over the eSPI bus interface.
+
+	  eSPI and LPC are mutually execulisve features on Aspeed SoC.
+	  If not sure, say N.
+
+config ASPEED_ESPI_PERIPHERAL
+	tristate "Aspeed eSPI peripheral channel driver"
+	depends on ASPEED_ESPI
+	help
+	  Control Aspeed eSPI peripheral channel driver. The driver
+	  also provides an eSPI packet put/get interface to communicate
+	  with the eSPI host.
+
+config ASPEED_ESPI_VW
+	tristate "Aspeed eSPI virtual wire channel driver"
+	depends on ASPEED_ESPI
+	help
+	  Control Aspeed eSPI virtual wire channel driver. The driver
+	  also provides an eSPI packet put/get interface to communicate
+	  with the eSPI host.
+
+config ASPEED_ESPI_OOB
+	tristate "Aspeed eSPI out-of-band channel driver"
+	depends on ASPEED_ESPI
+	help
+	  Control Aspeed eSPI out-of-band channel driver. The driver
+	  also provides an eSPI packet put/get interface to communicat
+	  with the eSPI host.
+
+config ASPEED_ESPI_FLASH
+	tristate "Aspeed eSPI flash channel driver"
+	depends on ASPEED_ESPI
+	help
+	  Control Aspeed eSPI flash channel driver. The driver
+	  also provides an eSPI packet put/get interface to communicat
+	  with the eSPI host.
+
 endmenu
 
 endif
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index fcab7192e1a4..ac41ce82bb78 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -3,3 +3,8 @@ 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 \
+				   aspeed-espi-peripheral.o \
+				   aspeed-espi-vw.o \
+				   aspeed-espi-oob.o \
+				   aspeed-espi-flash.o
diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.c b/drivers/soc/aspeed/aspeed-espi-ctrl.c
new file mode 100644
index 000000000000..e4329f5f8ed3
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-ctrl.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ */
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <soc/aspeed/espi.h>
+
+#define DEVICE_NAME "aspeed-espi-ctrl"
+
+struct aspeed_espi_ctrl {
+	struct regmap *map;
+	struct clk *clk;
+	struct reset_control *rst;
+
+	int irq;
+	int irq_reset;
+};
+
+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_HW_RST_DEASSERT))
+		return IRQ_NONE;
+
+	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 irqreturn_t aspeed_espi_ctrl_reset_isr(int irq, void *arg)
+{
+	struct aspeed_espi_ctrl *espi_ctrl = (struct aspeed_espi_ctrl *)arg;
+
+	disable_irq(espi_ctrl->irq);
+
+	reset_control_assert(espi_ctrl->rst);
+	udelay(1);
+	reset_control_deassert(espi_ctrl->rst);
+
+	regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT,
+			   ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE,
+			   0);
+
+	regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff);
+
+	regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1_INT_T0,
+			   ESPI_SYSEVT1_INT_T0_SUSPEND_WARN,
+			   ESPI_SYSEVT1_INT_T0_SUSPEND_WARN);
+
+	regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1_INT_EN,
+			   ESPI_SYSEVT1_INT_EN_SUSPEND_WARN,
+			   ESPI_SYSEVT1_INT_EN_SUSPEND_WARN);
+
+	enable_irq(espi_ctrl->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_espi_ctrl_init(struct device *dev,
+				 struct aspeed_espi_ctrl *espi_ctrl)
+{
+	int rc;
+
+	regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff);
+
+	regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1_INT_T0,
+			   ESPI_SYSEVT1_INT_T0_SUSPEND_WARN,
+			   ESPI_SYSEVT1_INT_T0_SUSPEND_WARN);
+
+	regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1_INT_EN,
+			   ESPI_SYSEVT1_INT_EN_SUSPEND_WARN,
+			   ESPI_SYSEVT1_INT_EN_SUSPEND_WARN);
+
+	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;
+	}
+
+	rc = devm_request_irq(dev, espi_ctrl->irq_reset,
+			      aspeed_espi_ctrl_reset_isr,
+			      IRQF_SHARED, DEVICE_NAME, espi_ctrl);
+	if (rc) {
+		dev_err(dev, "failed to request reset IRQ\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int aspeed_espi_ctrl_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct aspeed_espi_ctrl *espi_ctrl;
+	struct device *dev;
+
+	dev = &pdev->dev;
+
+	espi_ctrl = devm_kzalloc(dev, sizeof(*espi_ctrl), GFP_KERNEL);
+	if (!espi_ctrl)
+		return -ENOMEM;
+
+	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->irq_reset = platform_get_irq(pdev, 1);
+	if (espi_ctrl->irq_reset < 0)
+		return espi_ctrl->irq_reset;
+
+	espi_ctrl->rst = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(espi_ctrl->rst)) {
+		dev_err(dev, "cannot get reset\n");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	rc = aspeed_espi_ctrl_init(dev, espi_ctrl);
+	if (rc)
+		return rc;
+
+	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);
+
+	devm_free_irq(dev, espi_ctrl->irq, espi_ctrl);
+	devm_free_irq(dev, espi_ctrl->irq_reset, espi_ctrl);
+	devm_kfree(dev, espi_ctrl);
+	return 0;
+}
+
+static const struct of_device_id aspeed_espi_ctrl_of_matches[] = {
+	{ .compatible = "aspeed,ast2600-espi-ctrl" },
+	{ },
+};
+
+static struct platform_driver aspeed_espi_ctrl_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = aspeed_espi_ctrl_of_matches,
+	},
+	.probe = aspeed_espi_ctrl_probe,
+	.remove = aspeed_espi_ctrl_remove,
+};
+
+module_platform_driver(aspeed_espi_ctrl_driver);
+
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("Control of Aspeed eSPI reset and clocks");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/aspeed/aspeed-espi-flash.c b/drivers/soc/aspeed/aspeed-espi-flash.c
new file mode 100644
index 000000000000..80b9a612cf6e
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-flash.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ */
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include <soc/aspeed/espi.h>
+#include <uapi/linux/aspeed-espi.h>
+
+#define DEVICE_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 {
+	struct regmap *map;
+
+	int irq;
+	int irq_reset;
+
+	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;
+
+	uint32_t version;
+};
+
+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;
+
+	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_flash->map, ESPI_FLASH_RX_CTRL, &reg);
+	cyc = (reg & ESPI_FLASH_RX_CTRL_CYC_MASK) >> ESPI_FLASH_RX_CTRL_CYC_SHIFT;
+	tag = (reg & ESPI_FLASH_RX_CTRL_TAG_MASK) >> ESPI_FLASH_RX_CTRL_TAG_SHIFT;
+	len = (reg & ESPI_FLASH_RX_CTRL_LEN_MASK) >> ESPI_FLASH_RX_CTRL_LEN_SHIFT;
+
+	/*
+	 * calculate the length of the rest part of the
+	 * eSPI packet to be read from HW and copied to
+	 * user space.
+	 */
+	switch (cyc) {
+	case ESPI_FLASH_READ:
+	case ESPI_FLASH_WRITE:
+	case ESPI_FLASH_ERASE:
+		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
+			  sizeof(struct espi_flash_rwe);
+		break;
+	case ESPI_FLASH_SUC_CMPLT_D_MIDDLE:
+	case ESPI_FLASH_SUC_CMPLT_D_FIRST:
+	case ESPI_FLASH_SUC_CMPLT_D_LAST:
+	case ESPI_FLASH_SUC_CMPLT_D_ONLY:
+		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
+			  sizeof(struct espi_flash_cmplt);
+		break;
+	case ESPI_FLASH_SUC_CMPLT:
+	case ESPI_FLASH_UNSUC_CMPLT:
+		pkt_len = len + sizeof(struct espi_flash_cmplt);
+		break;
+	default:
+		rc = -EFAULT;
+		goto unlock_mtx_n_out;
+	}
+
+	if (ioc->pkt_len < pkt_len) {
+		rc = -EINVAL;
+		goto unlock_mtx_n_out;
+	}
+
+	pkt = vmalloc(pkt_len);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto unlock_mtx_n_out;
+	}
+
+	hdr = (struct espi_comm_hdr *)pkt;
+	hdr->cyc = cyc;
+	hdr->tag = tag;
+	hdr->len_h = len >> 8;
+	hdr->len_l = len & 0xff;
+
+	if (espi_flash->dma_mode) {
+		memcpy(hdr + 1, espi_flash->dma.rx_virt,
+		       pkt_len - sizeof(*hdr));
+	} else {
+		for (i = sizeof(*hdr); i < pkt_len; ++i) {
+			regmap_read(espi_flash->map,
+				    ESPI_FLASH_RX_PORT, &reg);
+			pkt[i] = reg & 0xff;
+		}
+	}
+
+	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) {
+		rc = -EFAULT;
+		goto free_n_out;
+	}
+
+	spin_lock_irqsave(&espi_flash->lock, flags);
+
+	regmap_write_bits(espi_flash->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;
+
+	if (!mutex_trylock(&espi_flash->put_tx_mtx))
+		return -EAGAIN;
+
+	regmap_read(espi_flash->map, ESPI_FLASH_TX_CTRL, &reg);
+	if (reg & ESPI_FLASH_TX_CTRL_TRIGGER) {
+		rc = -EBUSY;
+		goto unlock_mtx_n_out;
+	}
+
+	pkt = vmalloc(ioc->pkt_len);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto unlock_mtx_n_out;
+	}
+
+	hdr = (struct espi_comm_hdr *)pkt;
+
+	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
+		rc = -EFAULT;
+		goto free_n_out;
+	}
+
+	/*
+	 * common header (i.e. cycle type, tag, and length)
+	 * part is written to HW registers
+	 */
+	if (espi_flash->dma_mode) {
+		memcpy(espi_flash->dma.tx_virt, hdr + 1,
+		       ioc->pkt_len - sizeof(*hdr));
+		dma_wmb();
+	} else {
+		for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
+			regmap_write(espi_flash->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_flash->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 irqreturn_t aspeed_espi_flash_isr(int irq, void *arg)
+{
+	uint32_t sts;
+	unsigned long flags;
+	struct aspeed_espi_flash *espi_flash = arg;
+
+	regmap_read(espi_flash->map, ESPI_INT_STS, &sts);
+
+	if (!(sts & ESPI_INT_STS_FLASH_BITS))
+		return IRQ_NONE;
+
+	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);
+	}
+
+	regmap_write(espi_flash->map, ESPI_INT_STS, sts & ESPI_INT_STS_FLASH_BITS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t aspeed_espi_flash_reset_isr(int irq, void *arg)
+{
+	struct aspeed_espi_flash *espi_flash = arg;
+	struct aspeed_espi_flash_dma *dma = &espi_flash->dma;
+
+	disable_irq(espi_flash->irq);
+
+	if (espi_flash->dma_mode) {
+		regmap_write(espi_flash->map, ESPI_FLASH_TX_DMA, dma->tx_addr);
+		regmap_write(espi_flash->map, ESPI_FLASH_RX_DMA, dma->rx_addr);
+		regmap_update_bits(espi_flash->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_update_bits(espi_flash->map, ESPI_CTRL,
+			   ESPI_CTRL_FLASH_SW_MODE_MASK,
+			   espi_flash->safs_mode << ESPI_CTRL_FLASH_SW_MODE_SHIFT);
+
+	regmap_update_bits(espi_flash->map, ESPI_CTRL,
+			   ESPI_CTRL_FLASH_SW_RDY,
+			   ESPI_CTRL_FLASH_SW_RDY);
+
+	enable_irq(espi_flash->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_espi_flash_dma_init(struct device *dev,
+				      struct aspeed_espi_flash *espi_flash)
+{
+	struct aspeed_espi_flash_dma *dma = &espi_flash->dma;
+
+	dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
+					  &dma->tx_addr, GFP_KERNEL);
+	if (!dma->tx_virt)
+		return -ENOMEM;
+
+	dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
+					  &dma->rx_addr, GFP_KERNEL);
+	if (!dma->rx_virt)
+		return -ENOMEM;
+
+	regmap_write(espi_flash->map, ESPI_FLASH_TX_DMA, dma->tx_addr);
+	regmap_write(espi_flash->map, ESPI_FLASH_RX_DMA, dma->rx_addr);
+	regmap_update_bits(espi_flash->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);
+
+	return 0;
+}
+
+static int aspeed_espi_flash_init(struct device *dev,
+				  struct aspeed_espi_flash *espi_flash)
+{
+	int rc;
+
+	if (espi_flash->dma_mode) {
+		rc = aspeed_espi_flash_dma_init(dev, espi_flash);
+		if (rc)
+			return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_flash->irq, aspeed_espi_flash_isr,
+			      0, DEVICE_NAME, espi_flash);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI flash channel irq\n");
+		return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_flash->irq_reset, aspeed_espi_flash_reset_isr,
+			      IRQF_SHARED, DEVICE_NAME, espi_flash);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI channel reset irq\n");
+		return rc;
+	}
+
+	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);
+
+	espi_flash->mdev.parent = dev;
+	espi_flash->mdev.minor = MISC_DYNAMIC_MINOR;
+	espi_flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", DEVICE_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 rc;
+	}
+
+	regmap_update_bits(espi_flash->map, ESPI_CTRL,
+			   ESPI_CTRL_FLASH_SW_MODE_MASK | ESPI_CTRL_FLASH_SW_RDY,
+			   (espi_flash->safs_mode << ESPI_CTRL_FLASH_SW_MODE_SHIFT) |
+			    ESPI_CTRL_FLASH_SW_RDY);
+
+	return 0;
+}
+
+static int aspeed_espi_flash_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct device *dev = &pdev->dev;
+	struct aspeed_espi_flash *espi_flash;
+
+	espi_flash = devm_kzalloc(dev, sizeof(*espi_flash), GFP_KERNEL);
+	if (!espi_flash)
+		return -ENOMEM;
+
+	espi_flash->map = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(espi_flash->map)) {
+		dev_err(dev, "cannot get regmap\n");
+		return PTR_ERR(espi_flash->map);
+	}
+
+	espi_flash->irq = platform_get_irq(pdev, 0);
+	if (espi_flash->irq < 0)
+		return espi_flash->irq;
+
+	espi_flash->irq_reset = platform_get_irq(pdev, 1);
+	if (espi_flash->irq_reset < 0)
+		return espi_flash->irq_reset;
+
+	espi_flash->version = (uint32_t)of_device_get_match_data(dev);
+
+	if (of_property_read_bool(dev->of_node, "dma-mode"))
+		espi_flash->dma_mode = 1;
+
+	of_property_read_u32(dev->of_node, "safs-mode", &espi_flash->safs_mode);
+	if (espi_flash->safs_mode >= SAFS_MODES) {
+		dev_err(dev, "invalid SAFS mode\n");
+		return -EINVAL;
+	}
+
+	rc = aspeed_espi_flash_init(dev, espi_flash);
+	if (rc)
+		return rc;
+
+	dev_set_drvdata(dev, espi_flash);
+
+	dev_info(dev, "module loaded\n");
+
+	return 0;
+}
+
+static int aspeed_espi_flash_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct aspeed_espi_flash *espi_flash = dev_get_drvdata(dev);
+	struct aspeed_espi_flash_dma *dma;
+
+	mutex_destroy(&espi_flash->put_tx_mtx);
+	mutex_destroy(&espi_flash->get_rx_mtx);
+
+	if (!espi_flash->dma_mode)
+		return 0;
+
+	dma = &espi_flash->dma;
+
+	if (dma->tx_virt)
+		dma_free_coherent(dev, PAGE_SIZE, dma->tx_virt, dma->tx_addr);
+
+	if (dma->rx_virt)
+		dma_free_coherent(dev, PAGE_SIZE, dma->rx_virt, dma->rx_addr);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_espi_flash_match[] = {
+	{ .compatible = "aspeed,ast2600-espi-flash" },
+	{ }
+};
+
+static struct platform_driver aspeed_espi_flash_driver = {
+	.driver = {
+		.name           = DEVICE_NAME,
+		.of_match_table = aspeed_espi_flash_match,
+	},
+	.probe  = aspeed_espi_flash_probe,
+	.remove = aspeed_espi_flash_remove,
+};
+
+module_platform_driver(aspeed_espi_flash_driver);
+
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("Control of Aspeed eSPI flash channel");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/aspeed/aspeed-espi-oob.c b/drivers/soc/aspeed/aspeed-espi-oob.c
new file mode 100644
index 000000000000..3565a0f1e403
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-oob.c
@@ -0,0 +1,706 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ */
+#include <linux/io.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#include <soc/aspeed/espi.h>
+#include <uapi/linux/aspeed-espi.h>
+
+#define DEVICE_NAME	"aspeed-espi-oob"
+
+#define OOB_DMA_DESC_MAX_NUM	1024
+#define OOB_DMA_UNLOCK		0x45535049
+
+/* TX DMA 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 {
+	struct regmap *map;
+
+	int irq;
+	int irq_reset;
+
+	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;
+};
+
+/* 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 rptr, wptr, sptr;
+	uint8_t *pkt;
+	uint32_t pkt_len;
+	struct espi_comm_hdr *hdr;
+	struct oob_rx_dma_desc *d;
+
+	regmap_read(espi_oob->map, ESPI_OOB_RX_DMA_WS_PTR, &reg);
+	wptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT;
+	sptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT;
+
+	regmap_read(espi_oob->map, ESPI_OOB_RX_DMA_RD_PTR, &rptr);
+
+	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;
+	}
+
+	/* make the 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;
+
+	spin_lock_irqsave(&espi_oob->lock, flags);
+
+	regmap_write(espi_oob->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;
+
+	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) {
+		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_oob->map, ESPI_OOB_RX_CTRL, &reg);
+	cyc = (reg & ESPI_OOB_RX_CTRL_CYC_MASK) >> ESPI_OOB_RX_CTRL_CYC_SHIFT;
+	tag = (reg & ESPI_OOB_RX_CTRL_TAG_MASK) >> ESPI_OOB_RX_CTRL_TAG_SHIFT;
+	len = (reg & ESPI_OOB_RX_CTRL_LEN_MASK) >> ESPI_OOB_RX_CTRL_LEN_SHIFT;
+
+	/*
+	 * calculate the length of the rest part of the
+	 * eSPI packet to be read from HW and copied to
+	 * user space.
+	 */
+	pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
+		  sizeof(struct espi_comm_hdr);
+
+	if (ioc->pkt_len < pkt_len) {
+		rc = -EINVAL;
+		goto unlock_mtx_n_out;
+	}
+
+	pkt = vmalloc(pkt_len);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto unlock_mtx_n_out;
+	}
+
+	hdr = (struct espi_comm_hdr *)pkt;
+	hdr->cyc = cyc;
+	hdr->tag = tag;
+	hdr->len_h = len >> 8;
+	hdr->len_l = len & 0xff;
+
+	for (i = sizeof(*hdr); i < pkt_len; ++i) {
+		regmap_read(espi_oob->map,
+			    ESPI_OOB_RX_PORT, &reg);
+		pkt[i] = reg & 0xff;
+	}
+
+	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) {
+		rc = -EFAULT;
+		goto free_n_out;
+	}
+
+	spin_lock_irqsave(&espi_oob->lock, flags);
+
+	regmap_write_bits(espi_oob->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;
+
+	pkt = vmalloc(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_oob->map, ESPI_OOB_TX_DMA_RD_PTR,
+		     ESPI_OOB_TX_DMA_RD_PTR_UPDATE);
+
+	regmap_read(espi_oob->map, ESPI_OOB_TX_DMA_RD_PTR, &rptr);
+	regmap_read(espi_oob->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_oob->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;
+
+	if (!mutex_trylock(&espi_oob->put_tx_mtx))
+		return -EBUSY;
+
+	if (espi_oob->dma_mode) {
+		rc = aspeed_espi_oob_dma_desc_put_tx(fp, ioc, espi_oob);
+		goto unlock_mtx_n_out;
+	}
+
+	regmap_read(espi_oob->map, ESPI_OOB_TX_CTRL, &reg);
+	if (reg & ESPI_OOB_TX_CTRL_TRIGGER) {
+		rc = -EBUSY;
+		goto unlock_mtx_n_out;
+	}
+
+	if (ioc->pkt_len > ESPI_PKT_LEN_MAX) {
+		rc = -EINVAL;
+		goto unlock_mtx_n_out;
+	}
+
+	pkt = vmalloc(ioc->pkt_len);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto unlock_mtx_n_out;
+	}
+
+	hdr = (struct espi_comm_hdr *)pkt;
+
+	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
+		rc = -EFAULT;
+		goto free_n_out;
+	}
+
+	/*
+	 * common header (i.e. cycle type, tag, and length)
+	 * part is written to HW registers
+	 */
+	for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
+		regmap_write(espi_oob->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_oob->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 irqreturn_t aspeed_espi_oob_isr(int irq, void *arg)
+{
+	uint32_t sts;
+	unsigned long flags;
+	struct aspeed_espi_oob *espi_oob = arg;
+
+	regmap_read(espi_oob->map, ESPI_INT_STS, &sts);
+
+	if (!(sts & ESPI_INT_STS_OOB_BITS))
+		return IRQ_NONE;
+
+	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);
+	}
+
+	regmap_write(espi_oob->map, ESPI_INT_STS, sts & ESPI_INT_STS_OOB_BITS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t aspeed_espi_oob_reset_isr(int irq, void *arg)
+{
+	int i;
+	struct aspeed_espi_oob *espi_oob = arg;
+	struct aspeed_espi_oob_dma *dma = &espi_oob->dma;
+
+	disable_irq(espi_oob->irq);
+
+	if (espi_oob->dma_mode) {
+		regmap_update_bits(espi_oob->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);
+
+		for (i = 0; i < dma->rx_desc_num; ++i)
+			dma->rx_desc[i].dirty = 0;
+
+		regmap_write(espi_oob->map, ESPI_OOB_TX_DMA, dma->tx_desc_addr);
+		regmap_write(espi_oob->map, ESPI_OOB_TX_DMA_RB_SIZE, dma->tx_desc_num);
+		regmap_write(espi_oob->map, ESPI_OOB_TX_DMA_RD_PTR, OOB_DMA_UNLOCK);
+		regmap_write(espi_oob->map, ESPI_OOB_TX_DMA_WR_PTR, 0);
+
+		regmap_write(espi_oob->map, ESPI_OOB_RX_DMA, dma->rx_desc_addr);
+		regmap_write(espi_oob->map, ESPI_OOB_RX_DMA_RB_SIZE, dma->rx_desc_num);
+		regmap_write(espi_oob->map, ESPI_OOB_RX_DMA_RD_PTR, OOB_DMA_UNLOCK);
+		regmap_write(espi_oob->map, ESPI_OOB_RX_DMA_WS_PTR, 0);
+
+		regmap_update_bits(espi_oob->map, ESPI_OOB_RX_DMA_WS_PTR,
+				   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN,
+				   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN);
+	} else
+		regmap_write(espi_oob->map, ESPI_OOB_RX_CTRL, ESPI_OOB_RX_CTRL_PEND_SERV);
+
+	regmap_update_bits(espi_oob->map, ESPI_CTRL,
+			   ESPI_CTRL_OOB_SW_RDY,
+			   ESPI_CTRL_OOB_SW_RDY);
+
+	enable_irq(espi_oob->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_espi_oob_dma_init(struct device *dev,
+				struct aspeed_espi_oob *espi_oob)
+{
+	int i;
+	struct aspeed_espi_oob_dma *dma = &espi_oob->dma;
+
+	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_dbg(dev, "cannot allocate DMA TX descriptor\n");
+		return -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_dbg(dev, "cannot allocate DMA RX descriptor\n");
+		return -ENOMEM;
+	}
+
+	dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->tx_desc_num,
+					  &dma->tx_addr, GFP_KERNEL);
+	if (!dma->tx_virt) {
+		dev_dbg(dev, "cannot allocate DMA TX buffer\n");
+		return -ENOMEM;
+	}
+
+	dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->rx_desc_num,
+					  &dma->rx_addr, GFP_KERNEL);
+	if (!dma->rx_virt) {
+		dev_dbg(dev, "cannot allocate DMA RX buffer\n");
+		return -ENOMEM;
+	}
+
+	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_update_bits(espi_oob->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);
+
+	regmap_write(espi_oob->map, ESPI_OOB_TX_DMA, dma->tx_desc_addr);
+	regmap_write(espi_oob->map, ESPI_OOB_TX_DMA_RB_SIZE, dma->tx_desc_num);
+
+	regmap_write(espi_oob->map, ESPI_OOB_RX_DMA, dma->rx_desc_addr);
+	regmap_write(espi_oob->map, ESPI_OOB_RX_DMA_RB_SIZE, dma->rx_desc_num);
+	regmap_update_bits(espi_oob->map, ESPI_OOB_RX_DMA_WS_PTR,
+			   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN,
+			   ESPI_OOB_RX_DMA_WS_PTR_RECV_EN);
+
+	return 0;
+}
+
+static int aspeed_espi_oob_init(struct device *dev,
+				struct aspeed_espi_oob *espi_oob)
+{
+	int rc;
+
+	if (espi_oob->dma_mode) {
+		rc = aspeed_espi_oob_dma_init(dev, espi_oob);
+		if (rc)
+			return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_oob->irq, aspeed_espi_oob_isr,
+			      0, DEVICE_NAME, espi_oob);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI out-of-band channel irq\n");
+		return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_oob->irq_reset, aspeed_espi_oob_reset_isr,
+			      IRQF_SHARED, DEVICE_NAME, espi_oob);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI channel reset irq\n");
+		return rc;
+	}
+
+	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);
+
+	espi_oob->mdev.parent = dev;
+	espi_oob->mdev.minor = MISC_DYNAMIC_MINOR;
+	espi_oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", DEVICE_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 rc;
+	}
+
+	regmap_update_bits(espi_oob->map, ESPI_CTRL,
+			   ESPI_CTRL_OOB_SW_RDY,
+			   ESPI_CTRL_OOB_SW_RDY);
+
+	return 0;
+}
+
+static int aspeed_espi_oob_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct aspeed_espi_oob *espi_oob;
+	struct aspeed_espi_oob_dma *dma;
+	struct device *dev = &pdev->dev;
+
+	espi_oob = devm_kzalloc(dev, sizeof(*espi_oob), GFP_KERNEL);
+	if (!espi_oob)
+		return -ENOMEM;
+
+	espi_oob->map = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(espi_oob->map)) {
+		dev_err(dev, "cannot get regmap\n");
+		return PTR_ERR(espi_oob->map);
+	}
+
+	espi_oob->irq = platform_get_irq(pdev, 0);
+	if (espi_oob->irq < 0)
+		return espi_oob->irq;
+
+	espi_oob->irq_reset = platform_get_irq(pdev, 1);
+	if (espi_oob->irq_reset < 0)
+		return espi_oob->irq_reset;
+
+	if (of_property_read_bool(dev->of_node, "dma-mode"))
+		espi_oob->dma_mode = 1;
+
+	if (espi_oob->dma_mode) {
+		dma = &espi_oob->dma;
+
+		of_property_read_u32(dev->of_node, "dma-tx-desc-num",
+				     &dma->tx_desc_num);
+		of_property_read_u32(dev->of_node, "dma-rx-desc-num",
+				     &dma->rx_desc_num);
+
+		if (!dma->tx_desc_num || dma->tx_desc_num >= OOB_DMA_DESC_MAX_NUM) {
+			dev_err(dev, "invalid number of TX DMA descriptors\n");
+			return -EINVAL;
+		}
+
+		if (!dma->rx_desc_num || dma->rx_desc_num >= OOB_DMA_DESC_MAX_NUM) {
+			dev_err(dev, "invalid number of RX DMA descriptors\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * DMA descriptors are consumed in the circular
+		 * queue paradigm. Therefore, one dummy slot is
+		 * reserved to detect the full condition.
+		 */
+		dma->tx_desc_num += 1;
+		dma->rx_desc_num += 1;
+	}
+
+	rc = aspeed_espi_oob_init(dev, espi_oob);
+	if (rc)
+		return rc;
+
+	dev_set_drvdata(dev, espi_oob);
+
+	dev_info(dev, "module loaded\n");
+
+	return 0;
+}
+
+static int aspeed_espi_oob_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct aspeed_espi_oob *espi_oob = dev_get_drvdata(dev);
+	struct aspeed_espi_oob_dma *dma;
+
+	mutex_destroy(&espi_oob->put_tx_mtx);
+	mutex_destroy(&espi_oob->get_rx_mtx);
+
+	if (!espi_oob->dma_mode)
+		return 0;
+
+	dma = &espi_oob->dma;
+
+	if (dma->tx_desc)
+		dma_free_coherent(dev, sizeof(*dma->tx_desc) * dma->tx_desc_num,
+				  dma->tx_desc, dma->tx_desc_addr);
+
+	if (dma->rx_desc)
+		dma_free_coherent(dev, sizeof(*dma->rx_desc) * dma->rx_desc_num,
+				  dma->rx_desc, dma->rx_desc_addr);
+
+	if (dma->tx_virt)
+		dma_free_coherent(dev, PAGE_SIZE * dma->tx_desc_num,
+				  dma->tx_virt, dma->tx_addr);
+
+	if (dma->rx_virt)
+		dma_free_coherent(dev, PAGE_SIZE * dma->rx_desc_num,
+				  dma->rx_virt, dma->rx_addr);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_espi_oob_match[] = {
+	{ .compatible = "aspeed,ast2600-espi-oob" },
+	{ }
+};
+
+static struct platform_driver aspeed_espi_oob_driver = {
+	.driver = {
+		.name           = DEVICE_NAME,
+		.of_match_table = aspeed_espi_oob_match,
+	},
+	.probe  = aspeed_espi_oob_probe,
+	.remove = aspeed_espi_oob_remove,
+};
+
+module_platform_driver(aspeed_espi_oob_driver);
+
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("Control of Aspeed eSPI out-of-band channel");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/aspeed/aspeed-espi-peripheral.c b/drivers/soc/aspeed/aspeed-espi-peripheral.c
new file mode 100644
index 000000000000..852456d7019e
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-peripheral.c
@@ -0,0 +1,613 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ */
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+
+#include <soc/aspeed/espi.h>
+#include <uapi/linux/aspeed-espi.h>
+
+#define DEVICE_NAME	"aspeed-espi-peripheral"
+
+#define 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 {
+	struct regmap *map;
+
+	int	irq;
+	int irq_reset;
+
+	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 rx_lock;
+	struct mutex pc_tx_lock;
+	struct mutex np_tx_lock;
+
+	struct miscdevice mdev;
+};
+
+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;
+	uint32_t reg;
+	uint32_t cyc, tag, len;
+	uint8_t *pkt;
+	uint32_t pkt_len;
+	struct espi_comm_hdr *hdr;
+	unsigned long flags;
+
+	if (!espi_perif->rx_ready) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -ENODATA;
+
+		rc = wait_event_interruptible(espi_perif->wq, espi_perif->rx_ready);
+		if (rc == -ERESTARTSYS)
+			return -EINTR;
+	}
+
+	/* common header (i.e. cycle type, tag, and length) is taken by HW */
+	regmap_read(espi_perif->map, ESPI_PERIF_PC_RX_CTRL, &reg);
+	cyc = (reg & ESPI_PERIF_PC_RX_CTRL_CYC_MASK) >> ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT;
+	tag = (reg & ESPI_PERIF_PC_RX_CTRL_TAG_MASK) >> ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT;
+	len = (reg & ESPI_PERIF_PC_RX_CTRL_LEN_MASK) >> ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT;
+
+	/*
+	 * calculate the length of the rest part of the
+	 * eSPI packet to be read from HW and copied to
+	 * user space.
+	 */
+	switch (cyc) {
+	case ESPI_PERIF_MSG:
+		pkt_len = len + sizeof(struct espi_perif_msg);
+		break;
+	case ESPI_PERIF_MSG_D:
+		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
+			  sizeof(struct espi_perif_msg);
+		break;
+	case ESPI_PERIF_SUC_CMPLT_D_MIDDLE:
+	case ESPI_PERIF_SUC_CMPLT_D_FIRST:
+	case ESPI_PERIF_SUC_CMPLT_D_LAST:
+	case ESPI_PERIF_SUC_CMPLT_D_ONLY:
+		pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) +
+			  sizeof(struct espi_perif_cmplt);
+		break;
+	case ESPI_PERIF_SUC_CMPLT:
+	case ESPI_PERIF_UNSUC_CMPLT:
+		pkt_len = len + sizeof(struct espi_perif_cmplt);
+		break;
+	default:
+		return -EFAULT;
+	}
+
+	if (ioc->pkt_len < pkt_len)
+		return -EINVAL;
+
+	pkt = vmalloc(pkt_len);
+	if (!pkt)
+		return -ENOMEM;
+
+	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_perif->map,
+				    ESPI_PERIF_PC_RX_PORT, &reg);
+			pkt[i] = reg & 0xff;
+		}
+	}
+
+	if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len))
+		return -EFAULT;
+
+	spin_lock_irqsave(&espi_perif->rx_lock, flags);
+
+	regmap_write_bits(espi_perif->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->rx_lock, flags);
+
+	return pkt_len;
+}
+
+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;
+
+	if (!mutex_trylock(&espi_perif->pc_tx_lock))
+		return -EAGAIN;
+
+	regmap_read(espi_perif->map, ESPI_PERIF_PC_TX_CTRL, &reg);
+	if (reg & ESPI_PERIF_PC_TX_CTRL_TRIGGER) {
+		rc = -EBUSY;
+		goto unlock_n_out;
+	}
+
+	pkt = vmalloc(ioc->pkt_len);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto unlock_n_out;
+	}
+
+	hdr = (struct espi_comm_hdr *)pkt;
+
+	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
+		rc = -EFAULT;
+		goto free_n_out;
+	}
+
+	/*
+	 * common header (i.e. cycle type, tag, and length)
+	 * part is written to HW registers
+	 */
+	if (espi_perif->dma_mode) {
+		memcpy(espi_perif->dma.pc_tx_virt, hdr + 1,
+		       ioc->pkt_len - sizeof(*hdr));
+		dma_wmb();
+	} else {
+		for (i = sizeof(*hdr); i < ioc->pkt_len; ++i)
+			regmap_write(espi_perif->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_perif->map, ESPI_PERIF_PC_TX_CTRL, reg);
+
+free_n_out:
+	vfree(pkt);
+
+unlock_n_out:
+	mutex_unlock(&espi_perif->pc_tx_lock);
+
+	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;
+
+	if (!mutex_trylock(&espi_perif->np_tx_lock))
+		return -EAGAIN;
+
+	regmap_read(espi_perif->map, ESPI_PERIF_NP_TX_CTRL, &reg);
+	if (reg & ESPI_PERIF_NP_TX_CTRL_TRIGGER) {
+		rc = -EBUSY;
+		goto unlock_n_out;
+	}
+
+	pkt = vmalloc(ioc->pkt_len);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto unlock_n_out;
+	}
+
+	hdr = (struct espi_comm_hdr *)pkt;
+
+	if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) {
+		rc = -EFAULT;
+		goto free_n_out;
+	}
+
+	/*
+	 * common header (i.e. cycle type, tag, and length)
+	 * part is written to HW registers
+	 */
+	if (espi_perif->dma_mode) {
+		memcpy(espi_perif->dma.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_perif->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_perif->map, ESPI_PERIF_NP_TX_CTRL, reg);
+
+free_n_out:
+	vfree(pkt);
+
+unlock_n_out:
+	mutex_unlock(&espi_perif->np_tx_lock);
+
+	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 (((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 irqreturn_t aspeed_espi_peripheral_isr(int irq, void *arg)
+{
+	uint32_t sts;
+	unsigned long flags;
+	struct aspeed_espi_perif *espi_perif = arg;
+
+	regmap_read(espi_perif->map, ESPI_INT_STS, &sts);
+
+	if (!(sts & ESPI_INT_STS_PERIF_BITS))
+		return IRQ_NONE;
+
+	if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) {
+		spin_lock_irqsave(&espi_perif->rx_lock, flags);
+		espi_perif->rx_ready = 1;
+		spin_unlock_irqrestore(&espi_perif->rx_lock, flags);
+
+		wake_up_interruptible(&espi_perif->wq);
+	}
+
+	regmap_write(espi_perif->map, ESPI_INT_STS, sts & ESPI_INT_STS_PERIF_BITS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t aspeed_espi_peripheral_reset_isr(int irq, void *arg)
+{
+	struct aspeed_espi_perif *espi_perif = arg;
+	struct aspeed_espi_perif_dma *dma = &espi_perif->dma;
+
+	disable_irq(espi_perif->irq);
+
+	if (espi_perif->dma_mode) {
+		regmap_write(espi_perif->map, ESPI_PERIF_PC_RX_DMA, dma->pc_rx_addr);
+		regmap_write(espi_perif->map, ESPI_PERIF_PC_TX_DMA, dma->pc_tx_addr);
+		regmap_write(espi_perif->map, ESPI_PERIF_NP_TX_DMA, dma->np_tx_addr);
+
+		regmap_update_bits(espi_perif->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_update_bits(espi_perif->map, ESPI_CTRL,
+			   ESPI_CTRL_PERIF_SW_RDY,
+			   ESPI_CTRL_PERIF_SW_RDY);
+
+	enable_irq(espi_perif->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_espi_peripheral_dma_init(struct platform_device *pdev,
+				       struct aspeed_espi_perif *espi_perif)
+{
+	struct device *dev = &pdev->dev;
+	struct aspeed_espi_perif_dma *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_dbg(dev, "cannot allocate posted TX DMA buffer\n");
+		return -ENOMEM;
+	}
+
+	dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
+					     &dma->pc_rx_addr, GFP_KERNEL);
+	if (!dma->pc_rx_virt) {
+		dev_dbg(dev, "cannot allocate posted RX DMA buffer\n");
+		return -ENOMEM;
+	}
+
+	dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE,
+			&dma->np_tx_addr, GFP_KERNEL);
+	if (!dma->np_tx_virt) {
+		dev_dbg(dev, "cannot allocate non-posted TX DMA buffer\n");
+		return -ENOMEM;
+	}
+
+	regmap_write(espi_perif->map, ESPI_PERIF_PC_RX_DMA, dma->pc_rx_addr);
+	regmap_write(espi_perif->map, ESPI_PERIF_PC_TX_DMA, dma->pc_tx_addr);
+	regmap_write(espi_perif->map, ESPI_PERIF_NP_TX_DMA, dma->np_tx_addr);
+
+	regmap_update_bits(espi_perif->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);
+
+	return 0;
+}
+
+static int aspeed_espi_peripheral_init(struct platform_device *pdev,
+				       struct aspeed_espi_perif *espi_perif)
+{
+	int rc;
+	struct device *dev = &pdev->dev;
+
+	espi_perif->mcyc_virt = dma_alloc_coherent(dev, espi_perif->mcyc_size,
+						   &espi_perif->mcyc_taddr, GFP_KERNEL);
+	if (!espi_perif->mcyc_virt) {
+		dev_dbg(dev, "cannot allocate memory cycle region\n");
+		return -ENOMEM;
+	}
+
+	regmap_write(espi_perif->map, ESPI_PERIF_PC_RX_MASK,
+		     espi_perif->mcyc_mask | ESPI_PERIF_PC_RX_MASK_CFG_WP);
+	regmap_update_bits(espi_perif->map, ESPI_CTRL2,
+			   ESPI_CTRL2_MEMCYC_RD_DIS | ESPI_CTRL2_MEMCYC_WR_DIS, 0);
+
+	regmap_write(espi_perif->map, ESPI_PERIF_PC_RX_SADDR,
+		     espi_perif->mcyc_saddr);
+	regmap_write(espi_perif->map, ESPI_PERIF_PC_RX_TADDR,
+		     espi_perif->mcyc_taddr);
+
+	if (espi_perif->dma_mode) {
+		rc = aspeed_espi_peripheral_dma_init(pdev, espi_perif);
+		if (rc)
+			return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_perif->irq, aspeed_espi_peripheral_isr,
+			      0, DEVICE_NAME, espi_perif);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI peripheral channel irq\n");
+		return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_perif->irq_reset, aspeed_espi_peripheral_reset_isr,
+			      IRQF_SHARED, DEVICE_NAME, espi_perif);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI channel reset irq\n");
+		return rc;
+	}
+
+	init_waitqueue_head(&espi_perif->wq);
+
+	spin_lock_init(&espi_perif->rx_lock);
+	mutex_init(&espi_perif->pc_tx_lock);
+	mutex_init(&espi_perif->np_tx_lock);
+
+	espi_perif->mdev.parent = dev;
+	espi_perif->mdev.minor = MISC_DYNAMIC_MINOR;
+	espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", DEVICE_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 rc;
+	}
+
+	regmap_update_bits(espi_perif->map, ESPI_CTRL,
+			   ESPI_CTRL_PERIF_SW_RDY,
+			   ESPI_CTRL_PERIF_SW_RDY);
+
+	return 0;
+}
+
+static int aspeed_espi_peripheral_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct device *dev = &pdev->dev;
+	struct aspeed_espi_perif *espi_perif;
+
+	espi_perif = devm_kzalloc(&pdev->dev, sizeof(*espi_perif), GFP_KERNEL);
+	if (!espi_perif)
+		return -ENOMEM;
+
+	espi_perif->map = syscon_node_to_regmap(
+			dev->parent->of_node);
+	if (IS_ERR(espi_perif->map)) {
+		dev_err(dev, "cannot get regmap\n");
+		return -ENODEV;
+	}
+
+	espi_perif->irq = platform_get_irq(pdev, 0);
+	if (espi_perif->irq < 0)
+		return espi_perif->irq;
+
+	espi_perif->irq_reset = platform_get_irq(pdev, 1);
+	if (espi_perif->irq_reset < 0)
+		return espi_perif->irq_reset;
+
+	rc = of_property_read_u32(dev->of_node, "memcyc,map-src-addr", &espi_perif->mcyc_saddr);
+	if (rc) {
+		dev_err(dev, "cannot get Host mapping address for memroy cycle\n");
+		return -ENODEV;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "memcyc,map-size", &espi_perif->mcyc_size);
+	if (rc) {
+		dev_err(dev, "cannot get size for memory cycle\n");
+		return -ENODEV;
+	}
+
+	if (espi_perif->mcyc_size < MEMCYC_SIZE_MIN)
+		espi_perif->mcyc_size = MEMCYC_SIZE_MIN;
+
+	espi_perif->mcyc_size = roundup_pow_of_two(espi_perif->mcyc_size);
+	espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1);
+
+	espi_perif->dma_mode = of_property_read_bool(pdev->dev.of_node,
+						     "dma-mode");
+
+	rc = aspeed_espi_peripheral_init(pdev, espi_perif);
+	if (rc)
+		return rc;
+
+	dev_set_drvdata(dev, espi_perif);
+
+	dev_info(dev, "module loaded\n");
+
+	return 0;
+}
+
+static int aspeed_espi_peripheral_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct aspeed_espi_perif *espi_perif = dev_get_drvdata(dev);
+	struct aspeed_espi_perif_dma *dma;
+
+	mutex_destroy(&espi_perif->pc_tx_lock);
+	mutex_destroy(&espi_perif->np_tx_lock);
+
+	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)
+		return 0;
+
+	dma = &espi_perif->dma;
+
+	if (dma->pc_tx_virt)
+		dma_free_coherent(dev, PAGE_SIZE, dma->pc_tx_virt,
+				  dma->pc_tx_addr);
+	if (dma->pc_rx_virt)
+		dma_free_coherent(dev, PAGE_SIZE, dma->pc_rx_virt,
+				  dma->pc_rx_addr);
+	if (dma->np_tx_virt)
+		dma_free_coherent(dev, PAGE_SIZE, dma->np_tx_virt,
+				  dma->np_tx_addr);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_espi_peripheral_match[] = {
+	{ .compatible = "aspeed,ast2600-espi-peripheral" },
+	{ }
+};
+
+static struct platform_driver aspeed_espi_peripheral_driver = {
+	.driver = {
+		.name           = DEVICE_NAME,
+		.of_match_table = aspeed_espi_peripheral_match,
+	},
+	.probe  = aspeed_espi_peripheral_probe,
+	.remove = aspeed_espi_peripheral_remove,
+};
+
+module_platform_driver(aspeed_espi_peripheral_driver);
+
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("Control of Aspeed eSPI peripheral channel");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/aspeed/aspeed-espi-vw.c b/drivers/soc/aspeed/aspeed-espi-vw.c
new file mode 100644
index 000000000000..60cf775b09ed
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-espi-vw.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ */
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+
+#include <soc/aspeed/espi.h>
+#include <uapi/linux/aspeed-espi.h>
+
+#define DEVICE_NAME	"aspeed-espi-vw"
+
+struct aspeed_espi_vw {
+	struct regmap *map;
+
+	int irq;
+	int irq_reset;
+
+	struct miscdevice mdev;
+};
+
+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);
+
+	switch (cmd) {
+	case ASPEED_ESPI_VW_GET_GPIO_VAL:
+		regmap_read(espi_vw->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_vw->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 irqreturn_t aspeed_espi_vw_isr(int irq, void *arg)
+{
+	uint32_t sts;
+	uint32_t sysevt_sts;
+
+	struct aspeed_espi_vw *espi_vw = arg;
+
+	regmap_read(espi_vw->map, ESPI_INT_STS, &sts);
+
+	if (!(sts & ESPI_INT_STS_VW_BITS))
+		return IRQ_NONE;
+
+	if (sts & ESPI_INT_STS_VW_SYSEVT) {
+		regmap_read(espi_vw->map, ESPI_SYSEVT_INT_STS, &sysevt_sts);
+		regmap_write(espi_vw->map, ESPI_SYSEVT_INT_STS, sysevt_sts);
+	}
+
+	if (sts & ESPI_INT_STS_VW_SYSEVT1) {
+		regmap_read(espi_vw->map, ESPI_SYSEVT1_INT_STS, &sysevt_sts);
+
+		if (sysevt_sts & ESPI_SYSEVT1_INT_STS_SUSPEND_WARN)
+			regmap_update_bits(espi_vw->map, ESPI_SYSEVT1,
+					   ESPI_SYSEVT1_SUSPEND_ACK,
+					   ESPI_SYSEVT1_SUSPEND_ACK);
+
+		regmap_write(espi_vw->map, ESPI_SYSEVT1_INT_STS, sysevt_sts);
+	}
+
+	regmap_write(espi_vw->map, ESPI_INT_STS, sts & ESPI_INT_STS_VW_BITS);
+
+	return IRQ_HANDLED;
+
+}
+
+static irqreturn_t aspeed_espi_vw_reset_isr(int irq, void *arg)
+{
+	struct aspeed_espi_vw *espi_vw = arg;
+
+	disable_irq(espi_vw->irq);
+
+	regmap_update_bits(espi_vw->map, ESPI_CTRL,
+			   ESPI_CTRL_VW_SW_RDY,
+			   ESPI_CTRL_VW_SW_RDY);
+
+	enable_irq(espi_vw->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_espi_vw_init(struct device *dev,
+		struct aspeed_espi_vw *espi_vw)
+{
+	int rc;
+
+	rc = devm_request_irq(dev, espi_vw->irq, aspeed_espi_vw_isr,
+			0, DEVICE_NAME, espi_vw);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI virtual wire channel irq\n");
+		return rc;
+	}
+
+	rc = devm_request_irq(dev, espi_vw->irq_reset, aspeed_espi_vw_reset_isr,
+			IRQF_SHARED, DEVICE_NAME, espi_vw);
+	if (rc) {
+		dev_err(dev, "cannot request eSPI channel reset irq\n");
+		return rc;
+	}
+
+	espi_vw->mdev.parent = dev;
+	espi_vw->mdev.minor = MISC_DYNAMIC_MINOR;
+	espi_vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", DEVICE_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 rc;
+	}
+
+	regmap_update_bits(espi_vw->map, ESPI_CTRL,
+			   ESPI_CTRL_VW_SW_RDY,
+			   ESPI_CTRL_VW_SW_RDY);
+
+	return 0;
+}
+
+static int aspeed_espi_vw_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct aspeed_espi_vw *espi_vw;
+	struct device *dev = &pdev->dev;
+
+	espi_vw = devm_kzalloc(dev, sizeof(*espi_vw), GFP_KERNEL);
+	if (!espi_vw)
+		return -ENOMEM;
+
+	espi_vw->map = syscon_node_to_regmap(
+			dev->parent->of_node);
+	if (IS_ERR(espi_vw->map)) {
+		dev_err(dev, "cannot get regmap\n");
+		return -ENODEV;
+	}
+
+	espi_vw->irq = platform_get_irq(pdev, 0);
+	if (espi_vw->irq < 0)
+		return espi_vw->irq;
+
+	espi_vw->irq_reset = platform_get_irq(pdev, 1);
+	if (espi_vw->irq_reset < 0)
+		return espi_vw->irq_reset;
+
+	rc = aspeed_espi_vw_init(dev, espi_vw);
+	if (rc)
+		return rc;
+
+	dev_set_drvdata(dev, espi_vw);
+
+	dev_info(dev, "module loaded\n");
+
+	return 0;
+}
+
+static int aspeed_espi_vw_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id aspeed_espi_vw_match[] = {
+	{ .compatible = "aspeed,ast2600-espi-vw" },
+	{ }
+};
+
+static struct platform_driver aspeed_espi_vw_driver = {
+	.driver = {
+		.name           = DEVICE_NAME,
+		.of_match_table = aspeed_espi_vw_match,
+	},
+	.probe  = aspeed_espi_vw_probe,
+	.remove = aspeed_espi_vw_remove,
+};
+
+module_platform_driver(aspeed_espi_vw_driver);
+
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("Control of eSPI virtual wire channel");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/aspeed-espi.h b/include/uapi/linux/aspeed-espi.h
new file mode 100644
index 000000000000..eb5633b33e66
--- /dev/null
+++ b/include/uapi/linux/aspeed-espi.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020 Aspeed Technology Inc.
+ * Author: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
+ */
+#ifndef _UAPI_LINUX_ASPEED_ESPI_H
+#define _UAPI_LINUX_ASPEED_ESPI_H
+
+#ifdef __KERNEL__
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#else
+#include <sys/ioctl.h>
+#include <stdint.h>
+#endif
+
+/*
+ * 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[];
+} __attribute__((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[];
+} __attribute__((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[];
+} __attribute__((packed));
+
+struct espi_perif_cmplt {
+	uint8_t cyc;
+	uint8_t len_h : 4;
+	uint8_t tag : 4;
+	uint8_t len_l;
+	uint8_t data[];
+} __attribute__((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[];
+} __attribute__((packed));
+
+struct espi_flash_cmplt {
+	uint8_t cyc;
+	uint8_t len_h : 4;
+	uint8_t tag : 4;
+	uint8_t len_l;
+	uint8_t data[];
+} __attribute__((packed));
+
+struct aspeed_espi_ioc {
+	uint32_t pkt_len;
+	uint8_t *pkt;
+};
+
+#define ESPI_PLD_LEN_MIN	(1UL << 6)
+#define ESPI_PLD_LEN_MAX	(1UL << 12)
+
+/*
+ * we choose the longest header and the max payload size
+ * based on the Intel specification to define the maximum
+ * eSPI packet length
+ */
+#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
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 6/6] ARM: dts: aspeed: Add AST2600 eSPI nodes
  2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
                   ` (4 preceding siblings ...)
  2021-01-06  5:59 ` [PATCH 5/6] soc: aspeed: Add eSPI driver Chia-Wei, Wang
@ 2021-01-06  5:59 ` Chia-Wei, Wang
  5 siblings, 0 replies; 17+ messages in thread
From: Chia-Wei, Wang @ 2021-01-06  5:59 UTC (permalink / raw)
  To: robh+dt, joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel
  Cc: BMC-SW

Add eSPI nodes for the device tree of Aspeed 6th generation SoCs.

Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
---
 arch/arm/boot/dts/aspeed-g6.dtsi | 57 ++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index 810b0676ab03..d457baf11e37 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -3,7 +3,9 @@
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/interrupt-controller/aspeed-scu-ic.h>
+#include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
 #include <dt-bindings/clock/ast2600-clock.h>
+#include <dt-bindings/gpio/aspeed-gpio.h>
 
 / {
 	model = "Aspeed BMC";
@@ -75,6 +77,61 @@
 		interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
 	};
 
+	espi: espi@1e6ee000 {
+		compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon";
+		reg = <0x1e6ee000 0x1000>;
+
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x0 0x1e6ee000 0x1000>;
+
+		espi_ic: espi-ic {
+			#interrupt-cells = <1>;
+			compatible = "aspeed,ast2600-espi-ic";
+			interrupts-extended = <&gic GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
+					      <&gpio0 ASPEED_GPIO(W, 7) IRQ_TYPE_EDGE_FALLING>;
+			interrupt-controller;
+			status = "disabled";
+		};
+
+		espi_ctrl: espi-ctrl {
+			compatible = "aspeed,ast2600-espi-ctrl";
+			interrupts-extended = <&espi_ic ASPEED_ESPI_IC_CTRL_EVENT>,
+					      <&espi_ic ASPEED_ESPI_IC_CTRL_RESET>;
+			clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
+			resets = <&syscon ASPEED_RESET_ESPI>;
+			status = "disabled";
+		};
+
+		espi_peripheral: espi-peripheral-channel {
+			compatible = "aspeed,ast2600-espi-peripheral";
+			interrupts-extended = <&espi_ic ASPEED_ESPI_IC_PERIF_EVENT>,
+					      <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+			status = "disabled";
+		};
+
+		espi_vw: espi-vw-channel {
+			compatible = "aspeed,ast2600-espi-vw";
+			interrupts-extended = <&espi_ic ASPEED_ESPI_IC_VW_EVENT>,
+					      <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+			status = "disabled";
+		};
+
+		espi_oob: espi-oob-channel {
+			compatible = "aspeed,ast2600-espi-oob";
+			interrupts-extended = <&espi_ic ASPEED_ESPI_IC_OOB_EVENT>,
+					      <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+			status = "disabled";
+		};
+
+		espi_flash: espi-flash-channel {
+			compatible = "aspeed,ast2600-espi-flash";
+			interrupts-extended = <&espi_ic ASPEED_ESPI_IC_FLASH_EVENT>,
+					      <&espi_ic ASPEED_ESPI_IC_CHAN_RESET>;
+			status = "disabled";
+		};
+	};
+
 	ahb {
 		compatible = "simple-bus";
 		#address-cells = <1>;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
  2021-01-06  5:59 ` [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller Chia-Wei, Wang
@ 2021-01-06 10:59   ` Marc Zyngier
  2021-01-07  2:59     ` ChiaWei Wang
  0 siblings, 1 reply; 17+ messages in thread
From: Marc Zyngier @ 2021-01-06 10:59 UTC (permalink / raw)
  To: Chia-Wei, Wang
  Cc: robh+dt, joel, andrew, tglx, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

On 2021-01-06 05:59, Chia-Wei, Wang wrote:
> The eSPI interrupt controller acts as a SW IRQ number
> decoder to correctly control/dispatch interrupts of
> the eSPI peripheral, virtual wire, out-of-band, and
> flash channels.
> 
> Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> ---
>  drivers/irqchip/Makefile             |   2 +-
>  drivers/irqchip/irq-aspeed-espi-ic.c | 251 ++++++++++++++++++++++++
>  include/soc/aspeed/espi.h            | 279 +++++++++++++++++++++++++++
>  3 files changed, 531 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/irqchip/irq-aspeed-espi-ic.c
>  create mode 100644 include/soc/aspeed/espi.h
> 
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 0ac93bfaec61..56da4a3123f8 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -86,7 +86,7 @@ obj-$(CONFIG_MVEBU_PIC)			+= irq-mvebu-pic.o
>  obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
>  obj-$(CONFIG_LS_EXTIRQ)			+= irq-ls-extirq.o
>  obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
> -obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
> irq-aspeed-scu-ic.o
> +obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
> irq-aspeed-scu-ic.o irq-aspeed-espi-ic.o
>  obj-$(CONFIG_STM32_EXTI) 		+= irq-stm32-exti.o
>  obj-$(CONFIG_QCOM_IRQ_COMBINER)		+= qcom-irq-combiner.o
>  obj-$(CONFIG_IRQ_UNIPHIER_AIDET)	+= irq-uniphier-aidet.o
> diff --git a/drivers/irqchip/irq-aspeed-espi-ic.c
> b/drivers/irqchip/irq-aspeed-espi-ic.c
> new file mode 100644
> index 000000000000..8a5cc8fe3f0c
> --- /dev/null
> +++ b/drivers/irqchip/irq-aspeed-espi-ic.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2020 Aspeed Technology Inc.
> + */
> +#include <linux/bitops.h>
> +#include <linux/module.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +
> +#include <soc/aspeed/espi.h>
> +#include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
> +
> +#define DEVICE_NAME	"aspeed-espi-ic"
> +#define IRQCHIP_NAME	"eSPI-IC"
> +
> +#define ESPI_IC_IRQ_NUM	7
> +
> +struct aspeed_espi_ic {
> +	struct regmap *map;
> +	int irq;
> +	int gpio_irq;
> +	struct irq_domain *irq_domain;
> +};
> +
> +static void aspeed_espi_ic_gpio_isr(struct irq_desc *desc)
> +{
> +	unsigned int irq;
> +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +
> +	chained_irq_enter(chip, desc);
> +
> +	irq = irq_find_mapping(espi_ic->irq_domain,
> +				   ASPEED_ESPI_IC_CTRL_RESET);
> +	generic_handle_irq(irq);
> +
> +	irq = irq_find_mapping(espi_ic->irq_domain,
> +				   ASPEED_ESPI_IC_CHAN_RESET);
> +	generic_handle_irq(irq);

So for each mux interrupt, you generate two endpoints interrupt,
without even checking whether they are pending? That's no good.

> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void aspeed_espi_ic_isr(struct irq_desc *desc)
> +{
> +	unsigned int sts;
> +	unsigned int irq;
> +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +
> +	chained_irq_enter(chip, desc);
> +
> +	regmap_read(espi_ic->map, ESPI_INT_STS, &sts);
> +
> +	if (sts & ESPI_INT_STS_PERIF_BITS) {
> +		irq = irq_find_mapping(espi_ic->irq_domain,
> +				       ASPEED_ESPI_IC_PERIF_EVENT);
> +		generic_handle_irq(irq);
> +	}
> +
> +	if (sts & ESPI_INT_STS_VW_BITS) {
> +		irq = irq_find_mapping(espi_ic->irq_domain,
> +				       ASPEED_ESPI_IC_VW_EVENT);
> +		generic_handle_irq(irq);
> +	}
> +
> +	if (sts & ESPI_INT_STS_OOB_BITS) {
> +		irq = irq_find_mapping(espi_ic->irq_domain,
> +				       ASPEED_ESPI_IC_OOB_EVENT);
> +		generic_handle_irq(irq);
> +	}
> +
> +	if (sts & ESPI_INT_STS_FLASH_BITS) {
> +		irq = irq_find_mapping(espi_ic->irq_domain,
> +				       ASPEED_ESPI_IC_FLASH_EVENT);
> +		generic_handle_irq(irq);
> +	}
> +
> +	if (sts & ESPI_INT_STS_HW_RST_DEASSERT) {
> +		irq = irq_find_mapping(espi_ic->irq_domain,
> +				       ASPEED_ESPI_IC_CTRL_EVENT);
> +		generic_handle_irq(irq);
> +	}

This is horrible. Why can't you just use fls() in a loop?

> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void aspeed_espi_ic_irq_disable(struct irq_data *data)
> +{
> +	struct aspeed_espi_ic *espi_ic = irq_data_get_irq_chip_data(data);
> +
> +	switch (data->hwirq) {
> +	case ASPEED_ESPI_IC_CTRL_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_HW_RST_DEASSERT,
> +				   0);
> +		break;
> +	case ASPEED_ESPI_IC_PERIF_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_PERIF_BITS, 0);
> +		break;
> +	case ASPEED_ESPI_IC_VW_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_VW_BITS, 0);
> +		break;
> +	case ASPEED_ESPI_IC_OOB_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_OOB_BITS, 0);
> +		break;
> +	case ASPEED_ESPI_IC_FLASH_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_FLASH_BITS, 0);
> +		break;
> +	}

Most of these are masking multiple events at once, which makes me
think that it really doesn't belong here...

> +}
> +
> +static void aspeed_espi_ic_irq_enable(struct irq_data *data)
> +{
> +	struct aspeed_espi_ic *espi_ic = irq_data_get_irq_chip_data(data);
> +
> +	switch (data->hwirq) {
> +	case ASPEED_ESPI_IC_CTRL_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_HW_RST_DEASSERT,
> +				   ESPI_INT_EN_HW_RST_DEASSERT);
> +		break;
> +	case ASPEED_ESPI_IC_PERIF_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_PERIF_BITS,
> +				   ESPI_INT_EN_PERIF_BITS);
> +		break;
> +	case ASPEED_ESPI_IC_VW_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_VW_BITS,
> +				   ESPI_INT_EN_VW_BITS);
> +		break;
> +	case ASPEED_ESPI_IC_OOB_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_OOB_BITS,
> +				   ESPI_INT_EN_OOB_BITS);
> +		break;
> +	case ASPEED_ESPI_IC_FLASH_EVENT:
> +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> +				   ESPI_INT_EN_FLASH_BITS,
> +				   ESPI_INT_EN_FLASH_BITS);
> +		break;
> +	}
> +}
> +
> +static struct irq_chip aspeed_espi_ic_chip = {
> +	.name = IRQCHIP_NAME,
> +	.irq_enable = aspeed_espi_ic_irq_enable,
> +	.irq_disable = aspeed_espi_ic_irq_disable,
> +};
> +
> +static int aspeed_espi_ic_map(struct irq_domain *domain, unsigned int 
> irq,
> +			     irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &aspeed_espi_ic_chip, 
> handle_simple_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops aspeed_espi_ic_domain_ops = {
> +	.map = aspeed_espi_ic_map,
> +};
> +
> +static int aspeed_espi_ic_probe(struct platform_device *pdev)
> +{
> +	struct device *dev;
> +	struct aspeed_espi_ic *espi_ic;
> +
> +	dev = &pdev->dev;
> +
> +	espi_ic = devm_kzalloc(dev, sizeof(*espi_ic), GFP_KERNEL);
> +	if (!espi_ic)
> +		return -ENOMEM;
> +
> +	espi_ic->map = syscon_node_to_regmap(dev->parent->of_node);
> +	if (IS_ERR(espi_ic->map)) {
> +		dev_err(dev, "cannot get regmap\n");
> +		return -ENODEV;
> +	}
> +
> +	espi_ic->irq = platform_get_irq(pdev, 0);
> +	if (espi_ic->irq < 0)
> +		return espi_ic->irq;
> +
> +	espi_ic->gpio_irq = platform_get_irq(pdev, 1);
> +	if (espi_ic->gpio_irq < 0)
> +		return espi_ic->gpio_irq;
> +
> +	espi_ic->irq_domain = irq_domain_add_linear(dev->of_node, 
> ESPI_IC_IRQ_NUM,
> +						    &aspeed_espi_ic_domain_ops,
> +						    espi_ic);
> +	if (!espi_ic->irq_domain) {
> +		dev_err(dev, "cannot to add irq domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	irq_set_chained_handler_and_data(espi_ic->irq,
> +					 aspeed_espi_ic_isr,
> +					 espi_ic);
> +
> +	irq_set_chained_handler_and_data(espi_ic->gpio_irq,
> +					 aspeed_espi_ic_gpio_isr,
> +					 espi_ic);
> +
> +	dev_set_drvdata(dev, espi_ic);
> +
> +	dev_info(dev, "eSPI IRQ controller initialized\n");
> +
> +	return 0;
> +}
> +
> +static int aspeed_espi_ic_remove(struct platform_device *pdev)
> +{
> +	struct aspeed_espi_ic *espi_ic = platform_get_drvdata(pdev);
> +
> +	irq_domain_remove(espi_ic->irq_domain);
> +	return 0;
> +}
> +
> +static const struct of_device_id aspeed_espi_ic_of_matches[] = {
> +	{ .compatible = "aspeed,ast2600-espi-ic" },
> +	{ },
> +};
> +
> +static struct platform_driver aspeed_espi_ic_driver = {
> +	.driver = {
> +		.name = DEVICE_NAME,
> +		.of_match_table = aspeed_espi_ic_of_matches,
> +	},
> +	.probe = aspeed_espi_ic_probe,
> +	.remove = aspeed_espi_ic_remove,
> +};
> +
> +module_platform_driver(aspeed_espi_ic_driver);
> +
> +MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
> +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
> +MODULE_DESCRIPTION("Aspeed eSPI interrupt controller");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/soc/aspeed/espi.h b/include/soc/aspeed/espi.h
> new file mode 100644
> index 000000000000..c9a4f51737ee
> --- /dev/null
> +++ b/include/soc/aspeed/espi.h
> @@ -0,0 +1,279 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020 Aspeed Technology Inc.
> + * Author: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
> + */
> +#ifndef _ASPEED_ESPI_H_
> +#define _ASPEED_ESPI_H_

[...]

If nothing else uses the data here, move it to the irqchip driver.

         M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller
  2021-01-06  5:59 ` [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller Chia-Wei, Wang
@ 2021-01-06 15:07   ` Rob Herring
  2021-01-07  2:31     ` ChiaWei Wang
  0 siblings, 1 reply; 17+ messages in thread
From: Rob Herring @ 2021-01-06 15:07 UTC (permalink / raw)
  To: Chia-Wei, Wang
  Cc: linux-aspeed, p.zabel, tglx, joel, openbmc, maz, andrew,
	devicetree, linux-arm-kernel, robh+dt, linux-kernel, BMC-SW

On Wed, 06 Jan 2021 13:59:34 +0800, Chia-Wei, Wang wrote:
> Add dt-bindings and the inclusion header for Aspeed eSPI controller.
> 
> Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> ---
>  .../devicetree/bindings/soc/aspeed/espi.yaml  | 252 ++++++++++++++++++
>  .../interrupt-controller/aspeed-espi-ic.h     |  15 ++
>  2 files changed, 267 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml
>  create mode 100644 include/dt-bindings/interrupt-controller/aspeed-espi-ic.h
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/soc/aspeed/espi.example.dts:45.35-36 syntax error
FATAL ERROR: Unable to parse input tree
make[1]: *** [scripts/Makefile.lib:344: Documentation/devicetree/bindings/soc/aspeed/espi.example.dt.yaml] Error 1
make: *** [Makefile:1370: dt_binding_check] Error 2

See https://patchwork.ozlabs.org/patch/1422809

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.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 5/6] soc: aspeed: Add eSPI driver
  2021-01-06  5:59 ` [PATCH 5/6] soc: aspeed: Add eSPI driver Chia-Wei, Wang
@ 2021-01-06 15:32   ` Rob Herring
  2021-01-07  2:35     ` ChiaWei Wang
  0 siblings, 1 reply; 17+ messages in thread
From: Rob Herring @ 2021-01-06 15:32 UTC (permalink / raw)
  To: Chia-Wei, Wang
  Cc: joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

On Wed, Jan 06, 2021 at 01:59:38PM +0800, Chia-Wei, Wang wrote:
> The Aspeed eSPI controller is slave device to communicate with
> the master through the Enhanced Serial Peripheral Interface (eSPI).
> All of the four eSPI channels, namely peripheral, virtual wire,
> out-of-band, and flash are supported.
> 
> Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> ---
>  drivers/soc/aspeed/Kconfig                  |  49 ++
>  drivers/soc/aspeed/Makefile                 |   5 +
>  drivers/soc/aspeed/aspeed-espi-ctrl.c       | 197 ++++++
>  drivers/soc/aspeed/aspeed-espi-flash.c      | 490 ++++++++++++++
>  drivers/soc/aspeed/aspeed-espi-oob.c        | 706 ++++++++++++++++++++
>  drivers/soc/aspeed/aspeed-espi-peripheral.c | 613 +++++++++++++++++
>  drivers/soc/aspeed/aspeed-espi-vw.c         | 211 ++++++
>  include/uapi/linux/aspeed-espi.h            | 160 +++++
>  8 files changed, 2431 insertions(+)
>  create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c
>  create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
>  create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
>  create mode 100644 drivers/soc/aspeed/aspeed-espi-peripheral.c
>  create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c

drivers/spi/ is the correct location for a SPI controller.

>  create mode 100644 include/uapi/linux/aspeed-espi.h

This userspace interface is not going to be accepted upstream.

I'd suggest you look at similar SPI flash capable SPI controller drivers 
upstream and model your driver after them. This looks like it needs 
major reworking.

Rob

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller
  2021-01-06 15:07   ` Rob Herring
@ 2021-01-07  2:31     ` ChiaWei Wang
  0 siblings, 0 replies; 17+ messages in thread
From: ChiaWei Wang @ 2021-01-07  2:31 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-aspeed, p.zabel, tglx, joel, openbmc, maz, andrew,
	devicetree, linux-arm-kernel, robh+dt, linux-kernel, BMC-SW

Hi Rob,

> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Wednesday, January 6, 2021 11:08 PM
> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> Subject: Re: [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller
> 
> On Wed, 06 Jan 2021 13:59:34 +0800, Chia-Wei, Wang wrote:
> > Add dt-bindings and the inclusion header for Aspeed eSPI controller.
> >
> > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> > ---
> >  .../devicetree/bindings/soc/aspeed/espi.yaml  | 252
> ++++++++++++++++++
> >  .../interrupt-controller/aspeed-espi-ic.h     |  15 ++
> >  2 files changed, 267 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/soc/aspeed/espi.yaml
> >  create mode 100644
> > include/dt-bindings/interrupt-controller/aspeed-espi-ic.h
> >
> 
> My bot found errors running 'make dt_binding_check' on your patch:
> 
> yamllint warnings/errors:
> 
> dtschema/dtc warnings/errors:
> Error:
> Documentation/devicetree/bindings/soc/aspeed/espi.example.dts:45.35-36
> syntax error FATAL ERROR: Unable to parse input tree
> make[1]: *** [scripts/Makefile.lib:344:
> Documentation/devicetree/bindings/soc/aspeed/espi.example.dt.yaml] Error 1
> make: *** [Makefile:1370: dt_binding_check] Error 2
> 
> See https://patchwork.ozlabs.org/patch/1422809
> 
> 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.

My 'make dt_binding_check' did not show the error.
I will update the tool as suggested to fix the error. Thanks.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 5/6] soc: aspeed: Add eSPI driver
  2021-01-06 15:32   ` Rob Herring
@ 2021-01-07  2:35     ` ChiaWei Wang
  2021-01-08  2:59       ` Joel Stanley
  0 siblings, 1 reply; 17+ messages in thread
From: ChiaWei Wang @ 2021-01-07  2:35 UTC (permalink / raw)
  To: Rob Herring
  Cc: joel, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

Hi Rob,

> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Wednesday, January 6, 2021 11:32 PM
> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> Subject: Re: [PATCH 5/6] soc: aspeed: Add eSPI driver
> 
> On Wed, Jan 06, 2021 at 01:59:38PM +0800, Chia-Wei, Wang wrote:
> > The Aspeed eSPI controller is slave device to communicate with the
> > master through the Enhanced Serial Peripheral Interface (eSPI).
> > All of the four eSPI channels, namely peripheral, virtual wire,
> > out-of-band, and flash are supported.
> >
> > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> > ---
> >  drivers/soc/aspeed/Kconfig                  |  49 ++
> >  drivers/soc/aspeed/Makefile                 |   5 +
> >  drivers/soc/aspeed/aspeed-espi-ctrl.c       | 197 ++++++
> >  drivers/soc/aspeed/aspeed-espi-flash.c      | 490 ++++++++++++++
> >  drivers/soc/aspeed/aspeed-espi-oob.c        | 706
> ++++++++++++++++++++
> >  drivers/soc/aspeed/aspeed-espi-peripheral.c | 613 +++++++++++++++++
> >  drivers/soc/aspeed/aspeed-espi-vw.c         | 211 ++++++
> >  include/uapi/linux/aspeed-espi.h            | 160 +++++
> >  8 files changed, 2431 insertions(+)
> >  create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c
> >  create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
> >  create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
> >  create mode 100644 drivers/soc/aspeed/aspeed-espi-peripheral.c
> >  create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c
> 
> drivers/spi/ is the correct location for a SPI controller.
> 
> >  create mode 100644 include/uapi/linux/aspeed-espi.h
> 
> This userspace interface is not going to be accepted upstream.
> 
> I'd suggest you look at similar SPI flash capable SPI controller drivers upstream
> and model your driver after them. This looks like it needs major reworking.
> 
eSPI resues the timing and electrical specification of SPI but runs completely different protocol.
Only the flash channel is related to SPI and the other 3 channels are for EC/BMC/SIO.
Therefore, an eSPI driver might not fit into the SPI model.

Chiawei

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
  2021-01-06 10:59   ` Marc Zyngier
@ 2021-01-07  2:59     ` ChiaWei Wang
  2021-01-07 10:17       ` Marc Zyngier
  0 siblings, 1 reply; 17+ messages in thread
From: ChiaWei Wang @ 2021-01-07  2:59 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: robh+dt, joel, andrew, tglx, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

Hi Marc,

> -----Original Message-----
> From: Marc Zyngier <maz@kernel.org>
> Sent: Wednesday, January 6, 2021 6:59 PM
> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> Subject: Re: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
> 
> On 2021-01-06 05:59, Chia-Wei, Wang wrote:
> > The eSPI interrupt controller acts as a SW IRQ number decoder to
> > correctly control/dispatch interrupts of the eSPI peripheral, virtual
> > wire, out-of-band, and flash channels.
> >
> > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> > ---
> >  drivers/irqchip/Makefile             |   2 +-
> >  drivers/irqchip/irq-aspeed-espi-ic.c | 251 ++++++++++++++++++++++++
> >  include/soc/aspeed/espi.h            | 279
> +++++++++++++++++++++++++++
> >  3 files changed, 531 insertions(+), 1 deletion(-)  create mode 100644
> > drivers/irqchip/irq-aspeed-espi-ic.c
> >  create mode 100644 include/soc/aspeed/espi.h
> >
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index
> > 0ac93bfaec61..56da4a3123f8 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -86,7 +86,7 @@ obj-$(CONFIG_MVEBU_PIC)			+=
> irq-mvebu-pic.o
> >  obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
> >  obj-$(CONFIG_LS_EXTIRQ)			+= irq-ls-extirq.o
> >  obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
> > -obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
> > irq-aspeed-scu-ic.o
> > +obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
> > irq-aspeed-scu-ic.o irq-aspeed-espi-ic.o
> >  obj-$(CONFIG_STM32_EXTI) 		+= irq-stm32-exti.o
> >  obj-$(CONFIG_QCOM_IRQ_COMBINER)		+= qcom-irq-combiner.o
> >  obj-$(CONFIG_IRQ_UNIPHIER_AIDET)	+= irq-uniphier-aidet.o
> > diff --git a/drivers/irqchip/irq-aspeed-espi-ic.c
> > b/drivers/irqchip/irq-aspeed-espi-ic.c
> > new file mode 100644
> > index 000000000000..8a5cc8fe3f0c
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-aspeed-espi-ic.c
> > @@ -0,0 +1,251 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (c) 2020 Aspeed Technology Inc.
> > + */
> > +#include <linux/bitops.h>
> > +#include <linux/module.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h>
> > +#include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include
> > +<linux/regmap.h> #include <linux/of.h> #include <linux/of_platform.h>
> > +
> > +#include <soc/aspeed/espi.h>
> > +#include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
> > +
> > +#define DEVICE_NAME	"aspeed-espi-ic"
> > +#define IRQCHIP_NAME	"eSPI-IC"
> > +
> > +#define ESPI_IC_IRQ_NUM	7
> > +
> > +struct aspeed_espi_ic {
> > +	struct regmap *map;
> > +	int irq;
> > +	int gpio_irq;
> > +	struct irq_domain *irq_domain;
> > +};
> > +
> > +static void aspeed_espi_ic_gpio_isr(struct irq_desc *desc) {
> > +	unsigned int irq;
> > +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> > +
> > +	chained_irq_enter(chip, desc);
> > +
> > +	irq = irq_find_mapping(espi_ic->irq_domain,
> > +				   ASPEED_ESPI_IC_CTRL_RESET);
> > +	generic_handle_irq(irq);
> > +
> > +	irq = irq_find_mapping(espi_ic->irq_domain,
> > +				   ASPEED_ESPI_IC_CHAN_RESET);
> > +	generic_handle_irq(irq);
> 
> So for each mux interrupt, you generate two endpoints interrupt, without even
> checking whether they are pending? That's no good.

As the eSPI IC driver is chained to Aspeed GPIO IC, the pending is checked in the gpio-aspeed.c

> > +
> > +	chained_irq_exit(chip, desc);
> > +}
> > +
> > +static void aspeed_espi_ic_isr(struct irq_desc *desc) {
> > +	unsigned int sts;
> > +	unsigned int irq;
> > +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> > +
> > +	chained_irq_enter(chip, desc);
> > +
> > +	regmap_read(espi_ic->map, ESPI_INT_STS, &sts);
> > +
> > +	if (sts & ESPI_INT_STS_PERIF_BITS) {
> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> > +				       ASPEED_ESPI_IC_PERIF_EVENT);
> > +		generic_handle_irq(irq);
> > +	}
> > +
> > +	if (sts & ESPI_INT_STS_VW_BITS) {
> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> > +				       ASPEED_ESPI_IC_VW_EVENT);
> > +		generic_handle_irq(irq);
> > +	}
> > +
> > +	if (sts & ESPI_INT_STS_OOB_BITS) {
> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> > +				       ASPEED_ESPI_IC_OOB_EVENT);
> > +		generic_handle_irq(irq);
> > +	}
> > +
> > +	if (sts & ESPI_INT_STS_FLASH_BITS) {
> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> > +				       ASPEED_ESPI_IC_FLASH_EVENT);
> > +		generic_handle_irq(irq);
> > +	}
> > +
> > +	if (sts & ESPI_INT_STS_HW_RST_DEASSERT) {
> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> > +				       ASPEED_ESPI_IC_CTRL_EVENT);
> > +		generic_handle_irq(irq);
> > +	}
> 
> This is horrible. Why can't you just use fls() in a loop?

The bits in the interrupt status register for a eSPI channel are not sequentially arranged.
Using fls() may invoke an eSPI channel ISR multiple times.
So I collected the bitmap for each channel, respectively, and call the ISR at once.

> 
> > +
> > +	chained_irq_exit(chip, desc);
> > +}
> > +
> > +static void aspeed_espi_ic_irq_disable(struct irq_data *data) {
> > +	struct aspeed_espi_ic *espi_ic = irq_data_get_irq_chip_data(data);
> > +
> > +	switch (data->hwirq) {
> > +	case ASPEED_ESPI_IC_CTRL_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_HW_RST_DEASSERT,
> > +				   0);
> > +		break;
> > +	case ASPEED_ESPI_IC_PERIF_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_PERIF_BITS, 0);
> > +		break;
> > +	case ASPEED_ESPI_IC_VW_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_VW_BITS, 0);
> > +		break;
> > +	case ASPEED_ESPI_IC_OOB_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_OOB_BITS, 0);
> > +		break;
> > +	case ASPEED_ESPI_IC_FLASH_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_FLASH_BITS, 0);
> > +		break;
> > +	}
> 
> Most of these are masking multiple events at once, which makes me think that
> it really doesn't belong here...
> 
> > +}
> > +
> > +static void aspeed_espi_ic_irq_enable(struct irq_data *data) {
> > +	struct aspeed_espi_ic *espi_ic = irq_data_get_irq_chip_data(data);
> > +
> > +	switch (data->hwirq) {
> > +	case ASPEED_ESPI_IC_CTRL_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_HW_RST_DEASSERT,
> > +				   ESPI_INT_EN_HW_RST_DEASSERT);
> > +		break;
> > +	case ASPEED_ESPI_IC_PERIF_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_PERIF_BITS,
> > +				   ESPI_INT_EN_PERIF_BITS);
> > +		break;
> > +	case ASPEED_ESPI_IC_VW_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_VW_BITS,
> > +				   ESPI_INT_EN_VW_BITS);
> > +		break;
> > +	case ASPEED_ESPI_IC_OOB_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_OOB_BITS,
> > +				   ESPI_INT_EN_OOB_BITS);
> > +		break;
> > +	case ASPEED_ESPI_IC_FLASH_EVENT:
> > +		regmap_update_bits(espi_ic->map, ESPI_INT_EN,
> > +				   ESPI_INT_EN_FLASH_BITS,
> > +				   ESPI_INT_EN_FLASH_BITS);
> > +		break;
> > +	}
> > +}
> > +
> > +static struct irq_chip aspeed_espi_ic_chip = {
> > +	.name = IRQCHIP_NAME,
> > +	.irq_enable = aspeed_espi_ic_irq_enable,
> > +	.irq_disable = aspeed_espi_ic_irq_disable, };
> > +
> > +static int aspeed_espi_ic_map(struct irq_domain *domain, unsigned int
> > irq,
> > +			     irq_hw_number_t hwirq)
> > +{
> > +	irq_set_chip_and_handler(irq, &aspeed_espi_ic_chip,
> > handle_simple_irq);
> > +	irq_set_chip_data(irq, domain->host_data);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct irq_domain_ops aspeed_espi_ic_domain_ops = {
> > +	.map = aspeed_espi_ic_map,
> > +};
> > +
> > +static int aspeed_espi_ic_probe(struct platform_device *pdev) {
> > +	struct device *dev;
> > +	struct aspeed_espi_ic *espi_ic;
> > +
> > +	dev = &pdev->dev;
> > +
> > +	espi_ic = devm_kzalloc(dev, sizeof(*espi_ic), GFP_KERNEL);
> > +	if (!espi_ic)
> > +		return -ENOMEM;
> > +
> > +	espi_ic->map = syscon_node_to_regmap(dev->parent->of_node);
> > +	if (IS_ERR(espi_ic->map)) {
> > +		dev_err(dev, "cannot get regmap\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	espi_ic->irq = platform_get_irq(pdev, 0);
> > +	if (espi_ic->irq < 0)
> > +		return espi_ic->irq;
> > +
> > +	espi_ic->gpio_irq = platform_get_irq(pdev, 1);
> > +	if (espi_ic->gpio_irq < 0)
> > +		return espi_ic->gpio_irq;
> > +
> > +	espi_ic->irq_domain = irq_domain_add_linear(dev->of_node,
> > ESPI_IC_IRQ_NUM,
> > +						    &aspeed_espi_ic_domain_ops,
> > +						    espi_ic);
> > +	if (!espi_ic->irq_domain) {
> > +		dev_err(dev, "cannot to add irq domain\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	irq_set_chained_handler_and_data(espi_ic->irq,
> > +					 aspeed_espi_ic_isr,
> > +					 espi_ic);
> > +
> > +	irq_set_chained_handler_and_data(espi_ic->gpio_irq,
> > +					 aspeed_espi_ic_gpio_isr,
> > +					 espi_ic);
> > +
> > +	dev_set_drvdata(dev, espi_ic);
> > +
> > +	dev_info(dev, "eSPI IRQ controller initialized\n");
> > +
> > +	return 0;
> > +}
> > +
> > +static int aspeed_espi_ic_remove(struct platform_device *pdev) {
> > +	struct aspeed_espi_ic *espi_ic = platform_get_drvdata(pdev);
> > +
> > +	irq_domain_remove(espi_ic->irq_domain);
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id aspeed_espi_ic_of_matches[] = {
> > +	{ .compatible = "aspeed,ast2600-espi-ic" },
> > +	{ },
> > +};
> > +
> > +static struct platform_driver aspeed_espi_ic_driver = {
> > +	.driver = {
> > +		.name = DEVICE_NAME,
> > +		.of_match_table = aspeed_espi_ic_of_matches,
> > +	},
> > +	.probe = aspeed_espi_ic_probe,
> > +	.remove = aspeed_espi_ic_remove,
> > +};
> > +
> > +module_platform_driver(aspeed_espi_ic_driver);
> > +
> > +MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
> > +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
> > +MODULE_DESCRIPTION("Aspeed eSPI interrupt controller");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/soc/aspeed/espi.h b/include/soc/aspeed/espi.h new
> > file mode 100644 index 000000000000..c9a4f51737ee
> > --- /dev/null
> > +++ b/include/soc/aspeed/espi.h
> > @@ -0,0 +1,279 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (c) 2020 Aspeed Technology Inc.
> > + * Author: Chia-Wei Wang <chiawei_wang@aspeedtech.com>  */ #ifndef
> > +_ASPEED_ESPI_H_ #define _ASPEED_ESPI_H_
> 
> [...]
> 
> If nothing else uses the data here, move it to the irqchip driver.

The header will be used by other eSPI driver files. 

Chiawei.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
  2021-01-07  2:59     ` ChiaWei Wang
@ 2021-01-07 10:17       ` Marc Zyngier
  2021-01-08  2:33         ` ChiaWei Wang
  0 siblings, 1 reply; 17+ messages in thread
From: Marc Zyngier @ 2021-01-07 10:17 UTC (permalink / raw)
  To: ChiaWei Wang
  Cc: robh+dt, joel, andrew, tglx, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

On 2021-01-07 02:59, ChiaWei Wang wrote:
> Hi Marc,
> 
>> -----Original Message-----
>> From: Marc Zyngier <maz@kernel.org>
>> Sent: Wednesday, January 6, 2021 6:59 PM
>> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
>> Subject: Re: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt 
>> controller
>> 
>> On 2021-01-06 05:59, Chia-Wei, Wang wrote:
>> > The eSPI interrupt controller acts as a SW IRQ number decoder to
>> > correctly control/dispatch interrupts of the eSPI peripheral, virtual
>> > wire, out-of-band, and flash channels.
>> >
>> > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
>> > ---
>> >  drivers/irqchip/Makefile             |   2 +-
>> >  drivers/irqchip/irq-aspeed-espi-ic.c | 251 ++++++++++++++++++++++++
>> >  include/soc/aspeed/espi.h            | 279
>> +++++++++++++++++++++++++++
>> >  3 files changed, 531 insertions(+), 1 deletion(-)  create mode 100644
>> > drivers/irqchip/irq-aspeed-espi-ic.c
>> >  create mode 100644 include/soc/aspeed/espi.h
>> >
>> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index
>> > 0ac93bfaec61..56da4a3123f8 100644
>> > --- a/drivers/irqchip/Makefile
>> > +++ b/drivers/irqchip/Makefile
>> > @@ -86,7 +86,7 @@ obj-$(CONFIG_MVEBU_PIC)			+=
>> irq-mvebu-pic.o
>> >  obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
>> >  obj-$(CONFIG_LS_EXTIRQ)			+= irq-ls-extirq.o
>> >  obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
>> > -obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
>> > irq-aspeed-scu-ic.o
>> > +obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
>> > irq-aspeed-scu-ic.o irq-aspeed-espi-ic.o
>> >  obj-$(CONFIG_STM32_EXTI) 		+= irq-stm32-exti.o
>> >  obj-$(CONFIG_QCOM_IRQ_COMBINER)		+= qcom-irq-combiner.o
>> >  obj-$(CONFIG_IRQ_UNIPHIER_AIDET)	+= irq-uniphier-aidet.o
>> > diff --git a/drivers/irqchip/irq-aspeed-espi-ic.c
>> > b/drivers/irqchip/irq-aspeed-espi-ic.c
>> > new file mode 100644
>> > index 000000000000..8a5cc8fe3f0c
>> > --- /dev/null
>> > +++ b/drivers/irqchip/irq-aspeed-espi-ic.c
>> > @@ -0,0 +1,251 @@
>> > +// SPDX-License-Identifier: GPL-2.0-or-later
>> > +/*
>> > + * Copyright (c) 2020 Aspeed Technology Inc.
>> > + */
>> > +#include <linux/bitops.h>
>> > +#include <linux/module.h>
>> > +#include <linux/irq.h>
>> > +#include <linux/irqchip.h>
>> > +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h>
>> > +#include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include
>> > +<linux/regmap.h> #include <linux/of.h> #include <linux/of_platform.h>
>> > +
>> > +#include <soc/aspeed/espi.h>
>> > +#include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
>> > +
>> > +#define DEVICE_NAME	"aspeed-espi-ic"
>> > +#define IRQCHIP_NAME	"eSPI-IC"
>> > +
>> > +#define ESPI_IC_IRQ_NUM	7
>> > +
>> > +struct aspeed_espi_ic {
>> > +	struct regmap *map;
>> > +	int irq;
>> > +	int gpio_irq;
>> > +	struct irq_domain *irq_domain;
>> > +};
>> > +
>> > +static void aspeed_espi_ic_gpio_isr(struct irq_desc *desc) {
>> > +	unsigned int irq;
>> > +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
>> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> > +
>> > +	chained_irq_enter(chip, desc);
>> > +
>> > +	irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				   ASPEED_ESPI_IC_CTRL_RESET);
>> > +	generic_handle_irq(irq);
>> > +
>> > +	irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				   ASPEED_ESPI_IC_CHAN_RESET);
>> > +	generic_handle_irq(irq);
>> 
>> So for each mux interrupt, you generate two endpoints interrupt, 
>> without even
>> checking whether they are pending? That's no good.
> 
> As the eSPI IC driver is chained to Aspeed GPIO IC, the pending is
> checked in the gpio-aspeed.c

That's not the place to do that.

> 
>> > +
>> > +	chained_irq_exit(chip, desc);
>> > +}
>> > +
>> > +static void aspeed_espi_ic_isr(struct irq_desc *desc) {
>> > +	unsigned int sts;
>> > +	unsigned int irq;
>> > +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
>> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> > +
>> > +	chained_irq_enter(chip, desc);
>> > +
>> > +	regmap_read(espi_ic->map, ESPI_INT_STS, &sts);
>> > +
>> > +	if (sts & ESPI_INT_STS_PERIF_BITS) {
>> > +		irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				       ASPEED_ESPI_IC_PERIF_EVENT);
>> > +		generic_handle_irq(irq);
>> > +	}
>> > +
>> > +	if (sts & ESPI_INT_STS_VW_BITS) {
>> > +		irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				       ASPEED_ESPI_IC_VW_EVENT);
>> > +		generic_handle_irq(irq);
>> > +	}
>> > +
>> > +	if (sts & ESPI_INT_STS_OOB_BITS) {
>> > +		irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				       ASPEED_ESPI_IC_OOB_EVENT);
>> > +		generic_handle_irq(irq);
>> > +	}
>> > +
>> > +	if (sts & ESPI_INT_STS_FLASH_BITS) {
>> > +		irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				       ASPEED_ESPI_IC_FLASH_EVENT);
>> > +		generic_handle_irq(irq);
>> > +	}
>> > +
>> > +	if (sts & ESPI_INT_STS_HW_RST_DEASSERT) {
>> > +		irq = irq_find_mapping(espi_ic->irq_domain,
>> > +				       ASPEED_ESPI_IC_CTRL_EVENT);
>> > +		generic_handle_irq(irq);
>> > +	}
>> 
>> This is horrible. Why can't you just use fls() in a loop?
> 
> The bits in the interrupt status register for a eSPI channel are not
> sequentially arranged.
> Using fls() may invoke an eSPI channel ISR multiple times.
> So I collected the bitmap for each channel, respectively, and call the
> ISR at once.

And that's equally wrong. You need to handle interrupts individually,
as they are different signal. If you are to implement an interrupt
controller, please do it properly.

Otherwise, get rid of it and move everything into your pet driver.
There is no need to do a half-baked job.

As it is, there is no way this code can be merged.

         M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
  2021-01-07 10:17       ` Marc Zyngier
@ 2021-01-08  2:33         ` ChiaWei Wang
  0 siblings, 0 replies; 17+ messages in thread
From: ChiaWei Wang @ 2021-01-08  2:33 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: robh+dt, joel, andrew, tglx, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

Hi Marc,

I will revise the patch as suggested.
Thanks for the feedback.

Chiawei

> -----Original Message-----
> From: Marc Zyngier <maz@kernel.org>
> Sent: Thursday, January 7, 2021 6:18 PM
> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> <BMC-SW@aspeedtech.com>
> Subject: Re: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller
> 
> On 2021-01-07 02:59, ChiaWei Wang wrote:
> > Hi Marc,
> >
> >> -----Original Message-----
> >> From: Marc Zyngier <maz@kernel.org>
> >> Sent: Wednesday, January 6, 2021 6:59 PM
> >> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> >> Subject: Re: [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt
> >> controller
> >>
> >> On 2021-01-06 05:59, Chia-Wei, Wang wrote:
> >> > The eSPI interrupt controller acts as a SW IRQ number decoder to
> >> > correctly control/dispatch interrupts of the eSPI peripheral,
> >> > virtual wire, out-of-band, and flash channels.
> >> >
> >> > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> >> > ---
> >> >  drivers/irqchip/Makefile             |   2 +-
> >> >  drivers/irqchip/irq-aspeed-espi-ic.c | 251 ++++++++++++++++++++++++
> >> >  include/soc/aspeed/espi.h            | 279
> >> +++++++++++++++++++++++++++
> >> >  3 files changed, 531 insertions(+), 1 deletion(-)  create mode
> >> > 100644 drivers/irqchip/irq-aspeed-espi-ic.c
> >> >  create mode 100644 include/soc/aspeed/espi.h
> >> >
> >> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> >> > index
> >> > 0ac93bfaec61..56da4a3123f8 100644
> >> > --- a/drivers/irqchip/Makefile
> >> > +++ b/drivers/irqchip/Makefile
> >> > @@ -86,7 +86,7 @@ obj-$(CONFIG_MVEBU_PIC)			+=
> >> irq-mvebu-pic.o
> >> >  obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
> >> >  obj-$(CONFIG_LS_EXTIRQ)			+= irq-ls-extirq.o
> >> >  obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
> >> > -obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o
> irq-aspeed-i2c-ic.o
> >> > irq-aspeed-scu-ic.o
> >> > +obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o
> irq-aspeed-i2c-ic.o
> >> > irq-aspeed-scu-ic.o irq-aspeed-espi-ic.o
> >> >  obj-$(CONFIG_STM32_EXTI) 		+= irq-stm32-exti.o
> >> >  obj-$(CONFIG_QCOM_IRQ_COMBINER)		+= qcom-irq-combiner.o
> >> >  obj-$(CONFIG_IRQ_UNIPHIER_AIDET)	+= irq-uniphier-aidet.o
> >> > diff --git a/drivers/irqchip/irq-aspeed-espi-ic.c
> >> > b/drivers/irqchip/irq-aspeed-espi-ic.c
> >> > new file mode 100644
> >> > index 000000000000..8a5cc8fe3f0c
> >> > --- /dev/null
> >> > +++ b/drivers/irqchip/irq-aspeed-espi-ic.c
> >> > @@ -0,0 +1,251 @@
> >> > +// SPDX-License-Identifier: GPL-2.0-or-later
> >> > +/*
> >> > + * Copyright (c) 2020 Aspeed Technology Inc.
> >> > + */
> >> > +#include <linux/bitops.h>
> >> > +#include <linux/module.h>
> >> > +#include <linux/irq.h>
> >> > +#include <linux/irqchip.h>
> >> > +#include <linux/irqchip/chained_irq.h> #include
> >> > +<linux/irqdomain.h> #include <linux/interrupt.h> #include
> >> > +<linux/mfd/syscon.h> #include <linux/regmap.h> #include
> >> > +<linux/of.h> #include <linux/of_platform.h>
> >> > +
> >> > +#include <soc/aspeed/espi.h>
> >> > +#include <dt-bindings/interrupt-controller/aspeed-espi-ic.h>
> >> > +
> >> > +#define DEVICE_NAME	"aspeed-espi-ic"
> >> > +#define IRQCHIP_NAME	"eSPI-IC"
> >> > +
> >> > +#define ESPI_IC_IRQ_NUM	7
> >> > +
> >> > +struct aspeed_espi_ic {
> >> > +	struct regmap *map;
> >> > +	int irq;
> >> > +	int gpio_irq;
> >> > +	struct irq_domain *irq_domain;
> >> > +};
> >> > +
> >> > +static void aspeed_espi_ic_gpio_isr(struct irq_desc *desc) {
> >> > +	unsigned int irq;
> >> > +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
> >> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> >> > +
> >> > +	chained_irq_enter(chip, desc);
> >> > +
> >> > +	irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				   ASPEED_ESPI_IC_CTRL_RESET);
> >> > +	generic_handle_irq(irq);
> >> > +
> >> > +	irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				   ASPEED_ESPI_IC_CHAN_RESET);
> >> > +	generic_handle_irq(irq);
> >>
> >> So for each mux interrupt, you generate two endpoints interrupt,
> >> without even checking whether they are pending? That's no good.
> >
> > As the eSPI IC driver is chained to Aspeed GPIO IC, the pending is
> > checked in the gpio-aspeed.c
> 
> That's not the place to do that.
> 
> >
> >> > +
> >> > +	chained_irq_exit(chip, desc);
> >> > +}
> >> > +
> >> > +static void aspeed_espi_ic_isr(struct irq_desc *desc) {
> >> > +	unsigned int sts;
> >> > +	unsigned int irq;
> >> > +	struct aspeed_espi_ic *espi_ic = irq_desc_get_handler_data(desc);
> >> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> >> > +
> >> > +	chained_irq_enter(chip, desc);
> >> > +
> >> > +	regmap_read(espi_ic->map, ESPI_INT_STS, &sts);
> >> > +
> >> > +	if (sts & ESPI_INT_STS_PERIF_BITS) {
> >> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				       ASPEED_ESPI_IC_PERIF_EVENT);
> >> > +		generic_handle_irq(irq);
> >> > +	}
> >> > +
> >> > +	if (sts & ESPI_INT_STS_VW_BITS) {
> >> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				       ASPEED_ESPI_IC_VW_EVENT);
> >> > +		generic_handle_irq(irq);
> >> > +	}
> >> > +
> >> > +	if (sts & ESPI_INT_STS_OOB_BITS) {
> >> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				       ASPEED_ESPI_IC_OOB_EVENT);
> >> > +		generic_handle_irq(irq);
> >> > +	}
> >> > +
> >> > +	if (sts & ESPI_INT_STS_FLASH_BITS) {
> >> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				       ASPEED_ESPI_IC_FLASH_EVENT);
> >> > +		generic_handle_irq(irq);
> >> > +	}
> >> > +
> >> > +	if (sts & ESPI_INT_STS_HW_RST_DEASSERT) {
> >> > +		irq = irq_find_mapping(espi_ic->irq_domain,
> >> > +				       ASPEED_ESPI_IC_CTRL_EVENT);
> >> > +		generic_handle_irq(irq);
> >> > +	}
> >>
> >> This is horrible. Why can't you just use fls() in a loop?
> >
> > The bits in the interrupt status register for a eSPI channel are not
> > sequentially arranged.
> > Using fls() may invoke an eSPI channel ISR multiple times.
> > So I collected the bitmap for each channel, respectively, and call the
> > ISR at once.
> 
> And that's equally wrong. You need to handle interrupts individually, as they
> are different signal. If you are to implement an interrupt controller, please do it
> properly.
> 
> Otherwise, get rid of it and move everything into your pet driver.
> There is no need to do a half-baked job.
> 
> As it is, there is no way this code can be merged.
> 
>          M.
> --
> Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 5/6] soc: aspeed: Add eSPI driver
  2021-01-07  2:35     ` ChiaWei Wang
@ 2021-01-08  2:59       ` Joel Stanley
  2021-01-08  3:09         ` Ryan Chen
  0 siblings, 1 reply; 17+ messages in thread
From: Joel Stanley @ 2021-01-08  2:59 UTC (permalink / raw)
  To: ChiaWei Wang, Jeremy Kerr
  Cc: Rob Herring, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

On Thu, 7 Jan 2021 at 02:39, ChiaWei Wang <chiawei_wang@aspeedtech.com> wrote:
>
> Hi Rob,
>
> > -----Original Message-----
> > From: Rob Herring <robh@kernel.org>
> > Sent: Wednesday, January 6, 2021 11:32 PM
> > To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> > Subject: Re: [PATCH 5/6] soc: aspeed: Add eSPI driver
> >
> > On Wed, Jan 06, 2021 at 01:59:38PM +0800, Chia-Wei, Wang wrote:
> > > The Aspeed eSPI controller is slave device to communicate with the
> > > master through the Enhanced Serial Peripheral Interface (eSPI).
> > > All of the four eSPI channels, namely peripheral, virtual wire,
> > > out-of-band, and flash are supported.
> > >
> > > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> > > ---
> > >  drivers/soc/aspeed/Kconfig                  |  49 ++
> > >  drivers/soc/aspeed/Makefile                 |   5 +
> > >  drivers/soc/aspeed/aspeed-espi-ctrl.c       | 197 ++++++
> > >  drivers/soc/aspeed/aspeed-espi-flash.c      | 490 ++++++++++++++
> > >  drivers/soc/aspeed/aspeed-espi-oob.c        | 706
> > ++++++++++++++++++++
> > >  drivers/soc/aspeed/aspeed-espi-peripheral.c | 613 +++++++++++++++++
> > >  drivers/soc/aspeed/aspeed-espi-vw.c         | 211 ++++++
> > >  include/uapi/linux/aspeed-espi.h            | 160 +++++
> > >  8 files changed, 2431 insertions(+)
> > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c
> > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
> > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
> > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-peripheral.c
> > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c
> >
> > drivers/spi/ is the correct location for a SPI controller.
> >
> > >  create mode 100644 include/uapi/linux/aspeed-espi.h
> >
> > This userspace interface is not going to be accepted upstream.
> >
> > I'd suggest you look at similar SPI flash capable SPI controller drivers upstream
> > and model your driver after them. This looks like it needs major reworking.
> >
> eSPI resues the timing and electrical specification of SPI but runs completely different protocol.
> Only the flash channel is related to SPI and the other 3 channels are for EC/BMC/SIO.
> Therefore, an eSPI driver might not fit into the SPI model.

I agree, the naming is confusing but eSPI doesn't belong in drivers/spi.

As it is a bus that is common to more than just the Aspeed BMC, we may
want to implement it as a new bus type that has devices hanging off
it, similar to FSI.

Cheers,

Joel

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 5/6] soc: aspeed: Add eSPI driver
  2021-01-08  2:59       ` Joel Stanley
@ 2021-01-08  3:09         ` Ryan Chen
  0 siblings, 0 replies; 17+ messages in thread
From: Ryan Chen @ 2021-01-08  3:09 UTC (permalink / raw)
  To: Joel Stanley, ChiaWei Wang, Jeremy Kerr
  Cc: Rob Herring, andrew, tglx, maz, p.zabel, linux-aspeed, openbmc,
	devicetree, linux-arm-kernel, linux-kernel, BMC-SW

> -----Original Message-----
> From: Joel Stanley <joel@jms.id.au>
> Sent: Friday, January 8, 2021 10:59 AM
> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>; Jeremy Kerr
> <jk@codeconstruct.com.au>
> Cc: Rob Herring <robh@kernel.org>; andrew@aj.id.au; tglx@linutronix.de;
> maz@kernel.org; p.zabel@pengutronix.de; linux-aspeed@lists.ozlabs.org;
> openbmc@lists.ozlabs.org; devicetree@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; BMC-SW
> <BMC-SW@aspeedtech.com>
> Subject: Re: [PATCH 5/6] soc: aspeed: Add eSPI driver
> 
> On Thu, 7 Jan 2021 at 02:39, ChiaWei Wang
> <chiawei_wang@aspeedtech.com> wrote:
> >
> > Hi Rob,
> >
> > > -----Original Message-----
> > > From: Rob Herring <robh@kernel.org>
> > > Sent: Wednesday, January 6, 2021 11:32 PM
> > > To: ChiaWei Wang <chiawei_wang@aspeedtech.com>
> > > Subject: Re: [PATCH 5/6] soc: aspeed: Add eSPI driver
> > >
> > > On Wed, Jan 06, 2021 at 01:59:38PM +0800, Chia-Wei, Wang wrote:
> > > > The Aspeed eSPI controller is slave device to communicate with the
> > > > master through the Enhanced Serial Peripheral Interface (eSPI).
> > > > All of the four eSPI channels, namely peripheral, virtual wire,
> > > > out-of-band, and flash are supported.
> > > >
> > > > Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
> > > > ---
> > > >  drivers/soc/aspeed/Kconfig                  |  49 ++
> > > >  drivers/soc/aspeed/Makefile                 |   5 +
> > > >  drivers/soc/aspeed/aspeed-espi-ctrl.c       | 197 ++++++
> > > >  drivers/soc/aspeed/aspeed-espi-flash.c      | 490 ++++++++++++++
> > > >  drivers/soc/aspeed/aspeed-espi-oob.c        | 706
> > > ++++++++++++++++++++
> > > >  drivers/soc/aspeed/aspeed-espi-peripheral.c | 613
> +++++++++++++++++
> > > >  drivers/soc/aspeed/aspeed-espi-vw.c         | 211 ++++++
> > > >  include/uapi/linux/aspeed-espi.h            | 160 +++++
> > > >  8 files changed, 2431 insertions(+)  create mode 100644
> > > > drivers/soc/aspeed/aspeed-espi-ctrl.c
> > > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.c
> > > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.c
> > > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-peripheral.c
> > > >  create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.c
> > >
> > > drivers/spi/ is the correct location for a SPI controller.
> > >
> > > >  create mode 100644 include/uapi/linux/aspeed-espi.h
> > >
> > > This userspace interface is not going to be accepted upstream.
> > >
> > > I'd suggest you look at similar SPI flash capable SPI controller
> > > drivers upstream and model your driver after them. This looks like it needs
> major reworking.
> > >
> > eSPI resues the timing and electrical specification of SPI but runs completely
> different protocol.
> > Only the flash channel is related to SPI and the other 3 channels are for
> EC/BMC/SIO.
> > Therefore, an eSPI driver might not fit into the SPI model.
> 
> I agree, the naming is confusing but eSPI doesn't belong in drivers/spi.
> 
> As it is a bus that is common to more than just the Aspeed BMC, we may want
> to implement it as a new bus type that has devices hanging off it, similar to
> FSI.
> 
The ASPEED eSPI controller driver is slave side device, not master side. I think it will be stay soc/aspeed first. 
Because is most SoC Chip related. 

Cheers,

Ryan

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2021-01-08  3:11 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-06  5:59 [PATCH 0/6] arm: aspeed: Add eSPI support Chia-Wei, Wang
2021-01-06  5:59 ` [PATCH 1/6] dt-bindings: aspeed: Add eSPI controller Chia-Wei, Wang
2021-01-06 15:07   ` Rob Herring
2021-01-07  2:31     ` ChiaWei Wang
2021-01-06  5:59 ` [PATCH 2/6] MAINTAINER: Add ASPEED eSPI driver entry Chia-Wei, Wang
2021-01-06  5:59 ` [PATCH 3/6] clk: ast2600: Add eSPI reset bit Chia-Wei, Wang
2021-01-06  5:59 ` [PATCH 4/6] irqchip/aspeed: Add Aspeed eSPI interrupt controller Chia-Wei, Wang
2021-01-06 10:59   ` Marc Zyngier
2021-01-07  2:59     ` ChiaWei Wang
2021-01-07 10:17       ` Marc Zyngier
2021-01-08  2:33         ` ChiaWei Wang
2021-01-06  5:59 ` [PATCH 5/6] soc: aspeed: Add eSPI driver Chia-Wei, Wang
2021-01-06 15:32   ` Rob Herring
2021-01-07  2:35     ` ChiaWei Wang
2021-01-08  2:59       ` Joel Stanley
2021-01-08  3:09         ` Ryan Chen
2021-01-06  5:59 ` [PATCH 6/6] ARM: dts: aspeed: Add AST2600 eSPI nodes Chia-Wei, Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).