From e6e186acacc16fd95251b2d0fb024993a66ffa0a Mon Sep 17 00:00:00 2001 From: Haiyue Wang 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 Signed-off-by: Jae Hyun Yoo Signed-off-by: James Feist Signed-off-by: Vernon Mauery --- .../devicetree/bindings/misc/aspeed,espi-slave.txt | 20 + Documentation/misc-devices/espi-slave.rst | 118 ++++++ arch/arm/boot/dts/aspeed-g5.dtsi | 4 + arch/arm/boot/dts/aspeed-g6.dtsi | 14 + drivers/misc/Kconfig | 8 + drivers/misc/Makefile | 1 + drivers/misc/aspeed-espi-slave.c | 468 +++++++++++++++++++++ include/dt-bindings/clock/ast2600-clock.h | 1 + 8 files changed, 634 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..f72d9ae32f3e --- /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 + +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 +`_ + + -- 17. Enhanced Serial Peripheral Interface + + +`Enhanced Serial Peripheral Interface (eSPI) +- Interface Base Specification (for Client and Server Platforms) +`_ diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 26d885d2bd8a..7b798c49cb95 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -336,6 +336,7 @@ clocks = <&syscon ASPEED_CLK_APB>; interrupt-controller; #interrupt-cells = <2>; + status = "disabled"; }; sgpio: sgpio@1e780200 { @@ -432,6 +433,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 825e64ce317a..315d35c15208 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -4,6 +4,7 @@ #include #include #include +#include / { model = "Aspeed BMC"; @@ -708,6 +709,19 @@ 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>; + resets = <&syscon ASPEED_RESET_ESPI>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_espi_default>, + <&pinctrl_espialt_default>; + }; + i2c: bus@1e78a000 { compatible = "simple-bus"; #address-cells = <1>; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 06a2b753cc7c..0f9b5a356c93 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -433,6 +433,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 b9e6d4c3e906..53864687e8fd 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -51,6 +51,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..87bc81948694 --- /dev/null +++ b/drivers/misc/aspeed-espi-slave.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2019, Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + struct reset_control *reset; + int irq; + int rst_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) { + if (priv->rst_irq < 0) { + 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); + } + + regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, + ASPEED_ESPI_CTRL_OOB_CHRDY, + ASPEED_ESPI_CTRL_OOB_CHRDY); + 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); +} + +static irqreturn_t aspeed_espi_reset_isr(int irq, void *arg) +{ + struct aspeed_espi *priv = arg; + + reset_control_assert(priv->reset); + reset_control_deassert(priv->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); + + regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, + ASPEED_ESPI_CTRL_OOB_CHRDY, 0); + + aspeed_espi_config_irq(priv); + + return IRQ_HANDLED; +} + +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; + + ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_espi_irq, 0, + "aspeed-espi-irq", priv); + if (ret) + return ret; + + if (of_device_is_compatible(pdev->dev.of_node, + "aspeed,ast2600-espi-slave")) { + priv->rst_irq = platform_get_irq(pdev, 1); + if (priv->rst_irq < 0) + return priv->rst_irq; + + ret = devm_request_irq(&pdev->dev, priv->rst_irq, + aspeed_espi_reset_isr, 0, + "aspeed-espi-rst-irq", priv); + if (ret) + return ret; + + priv->reset = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + } else { + priv->rst_irq = -ENOTSUPP; + } + + 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; + } + + aspeed_espi_config_irq(priv); + aspeed_espi_boot_ack(priv); + + 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 "); +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_DESCRIPTION("Aspeed eSPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h index 3d90582a813f..1e18364de03d 100644 --- a/include/dt-bindings/clock/ast2600-clock.h +++ b/include/dt-bindings/clock/ast2600-clock.h @@ -104,6 +104,7 @@ #define ASPEED_RESET_PECI 36 #define ASPEED_RESET_MII 35 #define ASPEED_RESET_I2C 34 +#define ASPEED_RESET_ESPI 32 #define ASPEED_RESET_H2X 31 #define ASPEED_RESET_GP_MCU 30 #define ASPEED_RESET_DP_MCU 29 -- 2.7.4