summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch667
1 files changed, 667 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
new file mode 100644
index 000000000..07283f54d
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
@@ -0,0 +1,667 @@
+From 6e55e28db5eed85b7717aa4fc92c064f11429f6d Mon Sep 17 00:00:00 2001
+From: Haiyue Wang <haiyue.wang@linux.intel.com>
+Date: Sat, 24 Feb 2018 11:12:32 +0800
+Subject: [PATCH] eSPI: add ASPEED AST2500 eSPI driver to boot a host with PCH
+ runs on eSPI
+
+When PCH works under eSPI mode, the PMC (Power Management Controller) in
+PCH is waiting for SUS_ACK from BMC after it alerts SUS_WARN. It is in
+dead loop if no SUS_ACK assert. This is the basic requirement for the BMC
+works as eSPI slave.
+
+Also for the host power on / off actions, from BMC side, the following VW
+(Virtual Wire) messages are done in firmware:
+1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS
+2. SUS_ACK
+3. OOB_RESET_ACK
+4. HOST_RESET_ACK
+
+Also, it provides monitoring interface of PLTRST_N signal through
+/dev/espi-pltrstn
+
+Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+---
+ .../devicetree/bindings/misc/aspeed,espi-slave.txt | 19 +
+ Documentation/misc-devices/espi-slave.rst | 118 ++++++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 4 +
+ drivers/misc/Kconfig | 8 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-espi-slave.c | 420 +++++++++++++++++++++
+ 6 files changed, 570 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+ create mode 100644 Documentation/misc-devices/espi-slave.rst
+ create mode 100644 drivers/misc/aspeed-espi-slave.c
+
+diff --git a/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+new file mode 100644
+index 000000000000..8660e2ffbb89
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+@@ -0,0 +1,19 @@
++ASPEED eSPI Slave Controller
++
++Required properties:
++ - compatible: must be one of:
++ - "aspeed,ast2500-espi-slave"
++
++ - reg: physical base address of the controller and length of memory mapped
++ region
++
++ - interrupts: interrupt generated by the controller
++
++Example:
++
++ espi: espi@1e6ee000 {
++ compatible = "aspeed,ast2500-espi-slave";
++ reg = <0x1e6ee000 0x100>;
++ interrupts = <23>;
++ status = "disabled";
++};
+diff --git a/Documentation/misc-devices/espi-slave.rst b/Documentation/misc-devices/espi-slave.rst
+new file mode 100644
+index 000000000000..887a69a7130a
+--- /dev/null
++++ b/Documentation/misc-devices/espi-slave.rst
+@@ -0,0 +1,118 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++==========
++eSPI Slave
++==========
++
++:Author: Haiyue Wang <haiyue.wang@linux.intel.com>
++
++The PCH (**eSPI master**) provides the eSPI to support connection of a
++BMC (**eSPI slave**) to the platform.
++
++The LPC and eSPI interfaces are mutually exclusive. Both use the same
++pins, but on power-up, a HW strap determines if the eSPI or the LPC bus
++is operational. Once selected, it’s not possible to change to the other
++interface.
++
++``eSPI Channels and Supported Transactions``
++ +------+---------------------+----------------------+--------------------+
++ | CH # | Channel | Posted Cycles | Non-Posted Cycles |
++ +======+=====================+======================+====================+
++ | 0 | Peripheral | Memory Write, | Memory Read, |
++ | | | Completions | I/O Read/Write |
++ +------+---------------------+----------------------+--------------------+
++ | 1 | Virtual Wire | Virtual Wire GET/PUT | N/A |
++ +------+---------------------+----------------------+--------------------+
++ | 2 | Out-of-Band Message | SMBus Packet GET/PUT | N/A |
++ +------+---------------------+----------------------+--------------------+
++ | 3 | Flash Access | N/A | Flash Read, Write, |
++ | | | | Erase |
++ +------+---------------------+----------------------+--------------------+
++ | N/A | General | Register Accesses | N/A |
++ +------+---------------------+----------------------+--------------------+
++
++Virtual Wire Channel (Channel 1) Overview
++-----------------------------------------
++
++The Virtual Wire channel uses a standard message format to communicate
++several types of signals between the components on the platform::
++
++ - Sideband and GPIO Pins: System events and other dedicated signals
++ between the PCH and eSPI slave. These signals are tunneled between the
++ two components over eSPI.
++
++ - Serial IRQ Interrupts: Interrupts are tunneled from the eSPI slave to
++ the PCH. Both edge and triggered interrupts are supported.
++
++When PCH runs on eSPI mode, from BMC side, the following VW messages are
++done in firmware::
++
++ 1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS
++ 2. SUS_ACK
++ 3. OOB_RESET_ACK
++ 4. HOST_RESET_ACK
++
++``eSPI Virtual Wires (VW)``
++ +----------------------+---------+---------------------------------------+
++ |Virtual Wire |PCH Pin |Comments |
++ | |Direction| |
++ +======================+=========+=======================================+
++ |SUS_WARN# |Output |PCH pin is a GPIO when eSPI is enabled.|
++ | | |eSPI controller receives as VW message.|
++ +----------------------+---------+---------------------------------------+
++ |SUS_ACK# |Input |PCH pin is a GPIO when eSPI is enabled.|
++ | | |eSPI controller receives as VW message.|
++ +----------------------+---------+---------------------------------------+
++ |SLAVE_BOOT_LOAD_DONE |Input |Sent when the BMC has completed its |
++ | | |boot process as an indication to |
++ | | |eSPI-MC to continue with the G3 to S0 |
++ | | |exit. |
++ | | |The eSPI Master waits for the assertion|
++ | | |of this virtual wire before proceeding |
++ | | |with the SLP_S5# deassertion. |
++ | | |The intent is that it is never changed |
++ | | |except on a G3 exit - it is reset on a |
++ | | |G3 entry. |
++ +----------------------+---------+---------------------------------------+
++ |SLAVE_BOOT_LOAD_STATUS|Input |Sent upon completion of the Slave Boot |
++ | | |Load from the attached flash. A stat of|
++ | | |1 indicates that the boot code load was|
++ | | |successful and that the integrity of |
++ | | |the image is intact. |
++ +----------------------+---------+---------------------------------------+
++ |HOST_RESET_WARN |Output |Sent from the MC just before the Host |
++ | | |is about to enter reset. Upon receiving|
++ | | |, the BMC must flush and quiesce its |
++ | | |upstream Peripheral Channel request |
++ | | |queues and assert HOST_RESET_ACK VWire.|
++ | | |The MC subsequently completes any |
++ | | |outstanding posted transactions or |
++ | | |completions and then disables the |
++ | | |Peripheral Channel via a write to |
++ | | |the Slave's Configuration Register. |
++ +----------------------+---------+---------------------------------------+
++ |HOST_RESET_ACK |Input |ACK for the HOST_RESET_WARN message |
++ +----------------------+---------+---------------------------------------+
++ |OOB_RESET_WARN |Output |Sent from the MC just before the OOB |
++ | | |processor is about to enter reset. Upon|
++ | | |receiving, the BMC must flush and |
++ | | |quiesce its OOB Channel upstream |
++ | | |request queues and assert OOB_RESET_ACK|
++ | | |VWire. The-MC subsequently completes |
++ | | |any outstanding posted transactions or |
++ | | |completions and then disables the OOB |
++ | | |Channel via a write to the Slave's |
++ | | |Configuration Register. |
++ +----------------------+---------+---------------------------------------+
++ |OOB_RESET_ACK |Input |ACK for OOB_RESET_WARN message |
++ +----------------------+---------+---------------------------------------+
++
++`Intel C620 Series Chipset Platform Controller Hub
++<https://www.intel.com/content/www/us/en/chipsets/c620-series-chipset-datasheet.html>`_
++
++ -- 17. Enhanced Serial Peripheral Interface
++
++
++`Enhanced Serial Peripheral Interface (eSPI)
++- Interface Base Specification (for Client and Server Platforms)
++<https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0.pdf>`_
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 88f75736fe48..26671cc4dbd5 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -317,6 +317,7 @@
+ clocks = <&syscon ASPEED_CLK_APB>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
++ status = "disabled";
+ };
+
+ sgpio: sgpio@1e780200 {
+@@ -413,6 +414,9 @@
+ reg = <0x1e6ee000 0x100>;
+ interrupts = <23>;
+ status = "disabled";
++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_espi_default>;
+ };
+
+ lpc: lpc@1e789000 {
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index d681b7201f8c..50814caba1d3 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -455,6 +455,14 @@ config VEXPRESS_SYSCFG
+ bus. System Configuration interface is one of the possible means
+ of generating transactions on this bus.
+
++config ASPEED_ESPI_SLAVE
++ depends on ARCH_ASPEED || COMPILE_TEST
++ depends on REGMAP_MMIO
++ tristate "Aspeed ast2500 eSPI slave device driver"
++ ---help---
++ Control Aspeed ast2500 eSPI slave controller to handle event
++ which needs the firmware's processing.
++
+ config PCI_ENDPOINT_TEST
+ depends on PCI
+ select CRC32
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index fdd404120ed8..f168e6713440 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/
+ obj-$(CONFIG_ECHO) += echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE) += cxl/
++obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o
+ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
+ obj-$(CONFIG_OCXL) += ocxl/
+ obj-y += cardreader/
+diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c
+new file mode 100644
+index 000000000000..b0fc01692d3a
+--- /dev/null
++++ b/drivers/misc/aspeed-espi-slave.c
+@@ -0,0 +1,420 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2015-2019, Intel Corporation.
++
++#include <linux/clk.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/sched/signal.h>
++#include <linux/spinlock.h>
++#include <linux/uaccess.h>
++
++#define ASPEED_ESPI_CTRL 0x00
++#define ASPEED_ESPI_CTRL_SW_RESET GENMASK(31, 24)
++#define ASPEED_ESPI_CTRL_OOB_CHRDY BIT(4)
++#define ASPEED_ESPI_INT_STS 0x08
++#define ASPEED_ESPI_HW_RESET BIT(31)
++#define ASPEED_ESPI_VW_SYSEVT1 BIT(22)
++#define ASPEED_ESPI_VW_SYSEVT BIT(8)
++#define ASPEED_ESPI_INT_EN 0x0C
++#define ASPEED_ESPI_DATA_PORT 0x28
++#define ASPEED_ESPI_SYSEVT_INT_EN 0x94
++#define ASPEED_ESPI_SYSEVT 0x98
++#define ASPEED_ESPI_SYSEVT_HOST_RST_ACK BIT(27)
++#define ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23)
++#define ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20)
++#define ASPEED_ESPI_SYSEVT_OOB_RST_ACK BIT(16)
++#define ASPEED_ESPI_SYSEVT_INT_T0 0x110
++#define ASPEED_ESPI_SYSEVT_INT_T1 0x114
++#define ASPEED_ESPI_SYSEVT_INT_T2 0x118
++#define ASPEED_ESPI_SYSEVT_INT_STS 0x11C
++#define ASPEED_ESPI_SYSEVT_HOST_RST_WARN BIT(8)
++#define ASPEED_ESPI_SYSEVT_OOB_RST_WARN BIT(6)
++#define ASPEED_ESPI_SYSEVT_PLTRSTN BIT(5)
++#define ASPEED_ESPI_SYSEVT1_INT_EN 0x100
++#define ASPEED_ESPI_SYSEVT1 0x104
++#define ASPEED_ESPI_SYSEVT1_SUS_ACK BIT(20)
++#define ASPEED_ESPI_SYSEVT1_INT_T0 0x120
++#define ASPEED_ESPI_SYSEVT1_INT_T1 0x124
++#define ASPEED_ESPI_SYSEVT1_INT_T2 0x128
++#define ASPEED_ESPI_SYSEVT1_INT_STS 0x12C
++#define ASPEED_ESPI_SYSEVT1_SUS_WARN BIT(0)
++
++#define ASPEED_ESPI_INT_MASK \
++ (ASPEED_ESPI_HW_RESET | \
++ ASPEED_ESPI_VW_SYSEVT1 | \
++ ASPEED_ESPI_VW_SYSEVT)
++
++/*
++ * Setup Interrupt Type / Enable of System Event from Master
++ * T2 T1 T0
++ * 1) HOST_RST_WARN : Dual Edge 1 0 0
++ * 2) OOB_RST_WARN : Dual Edge 1 0 0
++ * 3) PLTRSTN : Dual Edge 1 0 0
++ */
++#define ASPEED_ESPI_SYSEVT_INT_T0_MASK 0
++#define ASPEED_ESPI_SYSEVT_INT_T1_MASK 0
++#define ASPEED_ESPI_SYSEVT_INT_T2_MASK \
++ (ASPEED_ESPI_SYSEVT_HOST_RST_WARN | \
++ ASPEED_ESPI_SYSEVT_OOB_RST_WARN | \
++ ASPEED_ESPI_SYSEVT_PLTRSTN)
++#define ASPEED_ESPI_SYSEVT_INT_MASK \
++ (ASPEED_ESPI_SYSEVT_INT_T0_MASK | \
++ ASPEED_ESPI_SYSEVT_INT_T1_MASK | \
++ ASPEED_ESPI_SYSEVT_INT_T2_MASK)
++
++/*
++ * Setup Interrupt Type / Enable of System Event 1 from Master
++ * T2 T1 T0
++ * 1) SUS_WARN : Rising Edge 0 0 1
++ */
++#define ASPEED_ESPI_SYSEVT1_INT_T0_MASK ASPEED_ESPI_SYSEVT1_SUS_WARN
++#define ASPEED_ESPI_SYSEVT1_INT_T1_MASK 0
++#define ASPEED_ESPI_SYSEVT1_INT_T2_MASK 0
++#define ASPEED_ESPI_SYSEVT1_INT_MASK \
++ (ASPEED_ESPI_SYSEVT1_INT_T0_MASK | \
++ ASPEED_ESPI_SYSEVT1_INT_T1_MASK | \
++ ASPEED_ESPI_SYSEVT1_INT_T2_MASK)
++
++struct aspeed_espi {
++ struct regmap *map;
++ struct clk *clk;
++ struct device *dev;
++ int irq;
++
++ /* for PLTRST_N signal monitoring interface */
++ struct miscdevice pltrstn_miscdev;
++ spinlock_t pltrstn_lock; /* for PLTRST_N signal sampling */
++ wait_queue_head_t pltrstn_waitq;
++ char pltrstn;
++};
++
++static void aspeed_espi_sys_event(struct aspeed_espi *priv)
++{
++ u32 sts, evt;
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT_INT_STS, &sts);
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT, &evt);
++
++ dev_dbg(priv->dev, "sys: sts = %08x, evt = %08x\n", sts, evt);
++
++ if (!(evt & ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS)) {
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT,
++ evt | ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS |
++ ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE);
++ dev_dbg(priv->dev, "Setting espi slave boot done\n");
++ }
++ if (sts & ASPEED_ESPI_SYSEVT_HOST_RST_WARN &&
++ evt & ASPEED_ESPI_SYSEVT_HOST_RST_WARN) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT,
++ ASPEED_ESPI_SYSEVT_HOST_RST_ACK,
++ ASPEED_ESPI_SYSEVT_HOST_RST_ACK);
++ dev_dbg(priv->dev, "SYSEVT_HOST_RST_WARN: acked\n");
++ }
++ if (sts & ASPEED_ESPI_SYSEVT_OOB_RST_WARN &&
++ evt & ASPEED_ESPI_SYSEVT_OOB_RST_WARN) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT,
++ ASPEED_ESPI_SYSEVT_OOB_RST_ACK,
++ ASPEED_ESPI_SYSEVT_OOB_RST_ACK);
++ dev_dbg(priv->dev, "SYSEVT_OOB_RST_WARN: acked\n");
++ }
++ if (sts & ASPEED_ESPI_SYSEVT_PLTRSTN || priv->pltrstn == 'U') {
++ priv->pltrstn = (evt & ASPEED_ESPI_SYSEVT_PLTRSTN) ? '1' : '0';
++ wake_up_interruptible(&priv->pltrstn_waitq);
++ dev_dbg(priv->dev, "SYSEVT_PLTRSTN: %c\n", priv->pltrstn);
++ }
++
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_STS, sts);
++}
++
++static void aspeed_espi_sys_event1(struct aspeed_espi *priv)
++{
++ u32 sts, evt;
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1_INT_STS, &sts);
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1, &evt);
++
++ dev_dbg(priv->dev, "sys event1: sts = %08x, evt = %08x\n", sts, evt);
++
++ if (sts & ASPEED_ESPI_SYSEVT1_SUS_WARN &&
++ evt & ASPEED_ESPI_SYSEVT1_SUS_WARN) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT1,
++ ASPEED_ESPI_SYSEVT1_SUS_ACK,
++ ASPEED_ESPI_SYSEVT1_SUS_ACK);
++ dev_dbg(priv->dev, "SYSEVT1_SUS_WARN: acked\n");
++ }
++
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_STS, sts);
++}
++
++static void aspeed_espi_boot_ack(struct aspeed_espi *priv)
++{
++ u32 evt;
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT, &evt);
++ if (!(evt & ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS)) {
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT,
++ evt | ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS |
++ ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE);
++ dev_dbg(priv->dev, "Setting espi slave boot done\n");
++ }
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1, &evt);
++ if (evt & ASPEED_ESPI_SYSEVT1_SUS_WARN &&
++ !(evt & ASPEED_ESPI_SYSEVT1_SUS_ACK)) {
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1,
++ evt | ASPEED_ESPI_SYSEVT1_SUS_ACK);
++ dev_dbg(priv->dev, "Boot SYSEVT1_SUS_WARN: acked\n");
++ }
++}
++
++static irqreturn_t aspeed_espi_irq(int irq, void *arg)
++{
++ struct aspeed_espi *priv = arg;
++ u32 sts, sts_handled = 0;
++
++ regmap_read(priv->map, ASPEED_ESPI_INT_STS, &sts);
++
++ dev_dbg(priv->dev, "INT_STS: 0x%08x\n", sts);
++
++ if (sts & ASPEED_ESPI_VW_SYSEVT) {
++ aspeed_espi_sys_event(priv);
++ sts_handled |= ASPEED_ESPI_VW_SYSEVT;
++ }
++
++ if (sts & ASPEED_ESPI_VW_SYSEVT1) {
++ aspeed_espi_sys_event1(priv);
++ sts_handled |= ASPEED_ESPI_VW_SYSEVT1;
++ }
++
++ if (sts & ASPEED_ESPI_HW_RESET) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL,
++ ASPEED_ESPI_CTRL_SW_RESET, 0);
++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL,
++ ASPEED_ESPI_CTRL_SW_RESET,
++ ASPEED_ESPI_CTRL_SW_RESET);
++ aspeed_espi_boot_ack(priv);
++ sts_handled |= ASPEED_ESPI_HW_RESET;
++ }
++
++ regmap_write(priv->map, ASPEED_ESPI_INT_STS, sts);
++
++ return sts != sts_handled ? IRQ_NONE : IRQ_HANDLED;
++}
++
++static void aspeed_espi_config_irq(struct aspeed_espi *priv)
++{
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T0,
++ ASPEED_ESPI_SYSEVT_INT_T0_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T1,
++ ASPEED_ESPI_SYSEVT_INT_T1_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T2,
++ ASPEED_ESPI_SYSEVT_INT_T2_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_EN,
++ ASPEED_ESPI_SYSEVT_INT_MASK);
++
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T0,
++ ASPEED_ESPI_SYSEVT1_INT_T0_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T1,
++ ASPEED_ESPI_SYSEVT1_INT_T1_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T2,
++ ASPEED_ESPI_SYSEVT1_INT_T2_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_EN,
++ ASPEED_ESPI_SYSEVT1_INT_MASK);
++
++ regmap_write(priv->map, ASPEED_ESPI_INT_EN, ASPEED_ESPI_INT_MASK);
++
++ aspeed_espi_boot_ack(priv);
++}
++
++static inline struct aspeed_espi *to_aspeed_espi(struct file *filp)
++{
++ return container_of(filp->private_data, struct aspeed_espi,
++ pltrstn_miscdev);
++}
++
++static int aspeed_espi_pltrstn_open(struct inode *inode, struct file *filp)
++{
++ if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
++ return -EACCES;
++
++ return 0;
++}
++
++static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf,
++ size_t count, loff_t *offset)
++{
++ struct aspeed_espi *priv = to_aspeed_espi(filp);
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned long flags;
++ char old_sample;
++ int ret = 0;
++
++ spin_lock_irqsave(&priv->pltrstn_lock, flags);
++
++ add_wait_queue(&priv->pltrstn_waitq, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ old_sample = priv->pltrstn;
++
++ do {
++ char new_sample = priv->pltrstn;
++
++ if (filp->f_flags & O_NONBLOCK || old_sample != new_sample) {
++ ret = put_user(new_sample, (unsigned long __user *)buf);
++ if (!ret)
++ ret = sizeof(new_sample);
++ } else if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ }
++
++ if (!ret) {
++ spin_unlock_irqrestore(&priv->pltrstn_lock, flags);
++ schedule();
++ spin_lock_irqsave(&priv->pltrstn_lock, flags);
++ }
++ } while (!ret);
++
++ remove_wait_queue(&priv->pltrstn_waitq, &wait);
++ set_current_state(TASK_RUNNING);
++
++ spin_unlock_irqrestore(&priv->pltrstn_lock, flags);
++
++ return ret;
++}
++
++static const struct file_operations aspeed_espi_pltrstn_fops = {
++ .owner = THIS_MODULE,
++ .open = aspeed_espi_pltrstn_open,
++ .read = aspeed_espi_pltrstn_read,
++};
++
++static const struct regmap_config aspeed_espi_regmap_cfg = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = ASPEED_ESPI_SYSEVT1_INT_STS,
++};
++
++static int aspeed_espi_probe(struct platform_device *pdev)
++{
++ struct aspeed_espi *priv;
++ struct resource *res;
++ void __iomem *regs;
++ u32 ctrl;
++ int ret;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ dev_set_drvdata(&pdev->dev, priv);
++ priv->dev = &pdev->dev;
++
++ priv->map = devm_regmap_init_mmio(&pdev->dev, regs,
++ &aspeed_espi_regmap_cfg);
++ if (IS_ERR(priv->map))
++ return PTR_ERR(priv->map);
++
++ spin_lock_init(&priv->pltrstn_lock);
++ init_waitqueue_head(&priv->pltrstn_waitq);
++ priv->pltrstn = 'U'; /* means it's not reported yet from master */
++
++ priv->irq = platform_get_irq(pdev, 0);
++ if (priv->irq < 0)
++ return priv->irq;
++
++ aspeed_espi_config_irq(priv);
++
++ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_espi_irq, 0,
++ "aspeed-espi-irq", priv);
++ if (ret)
++ return ret;
++
++ priv->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ ret = PTR_ERR(priv->clk);
++ if (ret != -EPROBE_DEFER)
++ dev_err(&pdev->dev, "couldn't get clock\n");
++ return ret;
++ }
++ ret = clk_prepare_enable(priv->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "couldn't enable clock\n");
++ return ret;
++ }
++
++ /*
++ * We check that the regmap works on this very first access, but as this
++ * is an MMIO-backed regmap, subsequent regmap access is not going to
++ * fail and we skip error checks from this point.
++ */
++ ret = regmap_read(priv->map, ASPEED_ESPI_CTRL, &ctrl);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to read ctrl register\n");
++ goto err_clk_disable_out;
++ }
++ regmap_write(priv->map, ASPEED_ESPI_CTRL,
++ ctrl | ASPEED_ESPI_CTRL_OOB_CHRDY);
++
++ priv->pltrstn_miscdev.minor = MISC_DYNAMIC_MINOR;
++ priv->pltrstn_miscdev.name = "espi-pltrstn";
++ priv->pltrstn_miscdev.fops = &aspeed_espi_pltrstn_fops;
++ priv->pltrstn_miscdev.parent = &pdev->dev;
++
++ ret = misc_register(&priv->pltrstn_miscdev);
++ if (ret) {
++ dev_err(&pdev->dev, "Unable to register device\n");
++ goto err_clk_disable_out;
++ }
++
++ dev_info(&pdev->dev, "eSPI registered, irq %d\n", priv->irq);
++
++ return 0;
++
++err_clk_disable_out:
++ clk_disable_unprepare(priv->clk);
++
++ return ret;
++}
++
++static int aspeed_espi_remove(struct platform_device *pdev)
++{
++ struct aspeed_espi *priv = dev_get_drvdata(&pdev->dev);
++
++ misc_deregister(&priv->pltrstn_miscdev);
++ clk_disable_unprepare(priv->clk);
++
++ return 0;
++}
++
++static const struct of_device_id of_espi_match_table[] = {
++ { .compatible = "aspeed,ast2500-espi-slave" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, of_espi_match_table);
++
++static struct platform_driver aspeed_espi_driver = {
++ .driver = {
++ .name = KBUILD_MODNAME,
++ .of_match_table = of_match_ptr(of_espi_match_table),
++ },
++ .probe = aspeed_espi_probe,
++ .remove = aspeed_espi_remove,
++};
++module_platform_driver(aspeed_espi_driver);
++
++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("Aspeed eSPI driver");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+