diff options
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.patch | 700 |
1 files changed, 700 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..a82fefba0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch @@ -0,0 +1,700 @@ +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] Add AST2500 eSPI driver + +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> +Signed-off-by: Vernon Mauery <vernon.mauery@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 + + arch/arm/boot/dts/aspeed-g6.dtsi | 12 + + 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,20 @@ ++ASPEED eSPI Slave Controller ++ ++Required properties: ++ - compatible: must be one of: ++ - "aspeed,ast2500-espi-slave" ++ - "aspeed,ast2600-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/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 567f268a3032..48de17a24c74 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -3,6 +3,7 @@ + + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/ast2600-clock.h> ++#include <dt-bindings/gpio/aspeed-gpio.h> + + / { + model = "Aspeed BMC"; +@@ -512,6 +513,17 @@ + status = "disabled"; + }; + ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2600-espi-slave"; ++ reg = <0x1e6ee000 0x200>; ++ interrupts-extended = <&gic GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>, ++ <&gpio0 ASPEED_GPIO(W, 7) IRQ_TYPE_EDGE_FALLING>; ++ status = "disabled"; ++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_espi_default>; ++ }; ++ + i2c: bus@1e78a000 { + compatible = "simple-bus"; + #address-cells = <1>; +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,421 @@ ++// 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" }, ++ { .compatible = "aspeed,ast2600-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 + |