From patchwork Mon Dec 18 12:27:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Haiyue Wang X-Patchwork-Id: 850031 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z0gKp2g70z9s84 for ; Mon, 18 Dec 2017 23:27:58 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3z0gKp06sczDsQM for ; Mon, 18 Dec 2017 23:27:58 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=linux.intel.com (client-ip=192.55.52.88; helo=mga01.intel.com; envelope-from=haiyue.wang@linux.intel.com; receiver=) Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3z0gKN5lVszDrnZ for ; Mon, 18 Dec 2017 23:27:35 +1100 (AEDT) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 18 Dec 2017 04:27:32 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.45,422,1508828400"; d="scan'208";a="3301589" Received: from build0101.sh.intel.com ([10.239.164.138]) by fmsmga007.fm.intel.com with ESMTP; 18 Dec 2017 04:27:30 -0800 From: Haiyue Wang To: openbmc@lists.ozlabs.org, joel@jms.id.au Subject: [PATCH linux dev-4.10 v2] eSPI: add Aspeed AST2500 eSPI driver to boot a host with PCH runs on eSPI Date: Mon, 18 Dec 2017 20:27:45 +0800 Message-Id: <1513600065-11715-1-git-send-email-haiyue.wang@linux.intel.com> X-Mailer: git-send-email 2.7.4 MIME-Version: 1.0 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Haiyue Wang Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" The PCH (master) provides the eSPI to support connection of a BMC (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. [This patch add the basic function to boot a host whose PCH runs on eSPI] 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 +----------------------+---------+---------------------------------------+ |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 | +----------------------+---------+---------------------------------------+ Signed-off-by: Haiyue Wang --- .../devicetree/bindings/misc/aspeed-espi-slave.txt | 32 +++ drivers/misc/Kconfig | 11 + drivers/misc/Makefile | 1 + drivers/misc/aspeed-espi-slave.c | 264 +++++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/aspeed-espi-slave.txt 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 0000000..0db889b --- /dev/null +++ b/Documentation/devicetree/bindings/misc/aspeed-espi-slave.txt @@ -0,0 +1,32 @@ +Aspeed eSPI Slave Controller + +Enhanced Serial Peripheral Interface (eSPI) is an interface using pins of SPI, +but runs different protocol. + +Its interface supports peripheral, virtual wire, out-of-band, and flash sharing +channels. + + -- https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0.pdf + Enhanced Serial Peripheral Interface (eSPI) + Interface Base Specification (for Client and Server Platforms) + January 2016 + Revision 1.0 + +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/drivers/misc/Kconfig b/drivers/misc/Kconfig index 02ffdd1..8c0d791 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -766,6 +766,17 @@ config PANEL_BOOT_MESSAGE An empty message will only clear the display at driver init time. Any other printf()-formatted message is valid with newline and escape codes. +config ASPEED_ESPI_SLAVE + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP + tristate "Aspeed ast2500 eSPI slave device" + ---help--- + This allows host to access Baseboard Management Controller (BMC) over the + Enhanced Serial Peripheral Interface (eSPI) bus, which replaces the Low Pin + Count (LPC) bus. + + Its interface supports peripheral, virtual wire, out-of-band, and flash + sharing channels. + config ASPEED_LPC_CTRL depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index ab8af76..a648599 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PANEL) += panel.o +obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c new file mode 100644 index 0000000..5a258eb --- /dev/null +++ b/drivers/misc/aspeed-espi-slave.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012-2020, ASPEED Technology Inc. +// Copyright (c) 2015-2017, Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEVICE_NAME "aspeed-espi-slave" + + +#define ESPI_CTRL 0x00 +#define ESPI_CTRL_SW_RESET GENMASK(31, 24) +#define ESPI_CTRL_OOB_CHRDY BIT(4) +#define ESPI_ISR 0x08 +#define ESPI_ISR_HW_RESET BIT(31) +#define ESPI_ISR_VW_SYS_EVT1 BIT(22) +#define ESPI_ISR_VW_SYS_EVT BIT(8) +#define ESPI_IER 0x0C + +#define ESPI_SYS_IER 0x94 +#define ESPI_SYS_EVENT 0x98 +#define ESPI_SYS_INT_T0 0x110 +#define ESPI_SYS_INT_T1 0x114 +#define ESPI_SYS_INT_T2 0x118 +#define ESPI_SYS_ISR 0x11C +#define ESPI_SYSEVT_HOST_RST_ACK BIT(27) +#define ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23) +#define ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20) +#define ESPI_SYSEVT_OOB_RST_ACK BIT(16) +#define ESPI_SYSEVT_HOST_RST_WARN BIT(8) +#define ESPI_SYSEVT_OOB_RST_WARN BIT(6) +#define ESPI_SYSEVT_PLT_RST_N BIT(5) + +#define ESPI_SYS1_IER 0x100 +#define ESPI_SYS1_EVENT 0x104 +#define ESPI_SYS1_INT_T0 0x120 +#define ESPI_SYS1_INT_T1 0x124 +#define ESPI_SYS1_INT_T2 0x128 +#define ESPI_SYS1_ISR 0x12C +#define ESPI_SYSEVT1_SUS_ACK BIT(20) +#define ESPI_SYSEVT1_SUS_WARN BIT(0) + + +struct aspeed_espi_slave_data { + void __iomem *base; + int irq; +}; + + +static inline u32 espi_read(struct aspeed_espi_slave_data *priv, + u32 reg) +{ + return readl(priv->base + reg); +} + +static inline void espi_write(struct aspeed_espi_slave_data *priv, + u32 reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static inline void espi_update(struct aspeed_espi_slave_data *priv, + u32 reg, u32 mask, u32 val) +{ + u32 tmp = readl(priv->base + reg); + + tmp &= ~mask; + tmp |= val & mask; + + writel(tmp, priv->base + reg); +} + +static void aspeed_espi_slave_sys_event(struct aspeed_espi_slave_data *priv) +{ + u32 sts = espi_read(priv, ESPI_SYS_ISR); + u32 evt = espi_read(priv, ESPI_SYS_EVENT); + + if (sts & ESPI_SYSEVT_HOST_RST_WARN) + espi_update(priv, ESPI_SYS_EVENT, ESPI_SYSEVT_HOST_RST_ACK, + evt & ESPI_SYSEVT_HOST_RST_WARN + ? ESPI_SYSEVT_HOST_RST_ACK : 0); + + if (sts & ESPI_SYSEVT_OOB_RST_WARN) + espi_update(priv, ESPI_SYS_EVENT, ESPI_SYSEVT_OOB_RST_ACK, + evt & ESPI_SYSEVT_OOB_RST_WARN + ? ESPI_SYSEVT_OOB_RST_ACK : 0); + + espi_write(priv, ESPI_SYS_ISR, sts); +} + +static void aspeed_espi_slave_sys1_event(struct aspeed_espi_slave_data *priv) +{ + u32 sts = espi_read(priv, ESPI_SYS1_ISR); + + if (sts & ESPI_SYSEVT1_SUS_WARN) + espi_update(priv, ESPI_SYS1_EVENT, ESPI_SYSEVT1_SUS_ACK, + ESPI_SYSEVT1_SUS_ACK); + + espi_write(priv, ESPI_SYS1_ISR, sts); +} + +static irqreturn_t aspeed_espi_slave_irq(int irq, void *arg) +{ + struct aspeed_espi_slave_data *priv = arg; + u32 sts = espi_read(priv, ESPI_ISR); + + if (sts & ESPI_ISR_HW_RESET) { + espi_update(priv, ESPI_CTRL, ESPI_CTRL_SW_RESET, + 0); + espi_update(priv, ESPI_CTRL, ESPI_CTRL_SW_RESET, + ESPI_CTRL_SW_RESET); + + espi_update(priv, ESPI_SYS_EVENT, + ESPI_SYSEVT_SLAVE_BOOT_STATUS | + ESPI_SYSEVT_SLAVE_BOOT_DONE, + ESPI_SYSEVT_SLAVE_BOOT_STATUS | + ESPI_SYSEVT_SLAVE_BOOT_DONE); + } + + if (sts & ESPI_ISR_VW_SYS_EVT) + aspeed_espi_slave_sys_event(priv); + + if (sts & ESPI_ISR_VW_SYS_EVT1) + aspeed_espi_slave_sys1_event(priv); + + espi_write(priv, ESPI_ISR, sts); + + return IRQ_HANDLED; +} + +static int aspeed_espi_slave_config_irq(struct aspeed_espi_slave_data *priv, + struct platform_device *pdev) +{ + int rc; + struct device *dev = &pdev->dev; + + priv->irq = platform_get_irq(pdev, 0); + if (!priv->irq) + return -ENODEV; + + rc = devm_request_irq(dev, priv->irq, aspeed_espi_slave_irq, + IRQF_SHARED, DEVICE_NAME, priv); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", priv->irq); + priv->irq = 0; + return rc; + } + + espi_update(priv, ESPI_CTRL, ESPI_CTRL_OOB_CHRDY, ESPI_CTRL_OOB_CHRDY); + + /* 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). PLTRST_N : Dual Edge 1 0 0 + */ +#define ESPI_SYS_INT_T0_SET \ + 0x00000000 +#define ESPI_SYS_INT_T1_SET \ + 0x00000000 +#define ESPI_SYS_INT_T2_SET ( \ + ESPI_SYSEVT_HOST_RST_WARN | \ + ESPI_SYSEVT_OOB_RST_WARN | \ + ESPI_SYSEVT_PLT_RST_N) +#define ESPI_SYS_INT_SET ( \ + ESPI_SYSEVT_HOST_RST_WARN | \ + ESPI_SYSEVT_OOB_RST_WARN | \ + ESPI_SYSEVT_PLT_RST_N) + espi_write(priv, ESPI_SYS_INT_T0, ESPI_SYS_INT_T0_SET); + espi_write(priv, ESPI_SYS_INT_T1, ESPI_SYS_INT_T1_SET); + espi_write(priv, ESPI_SYS_INT_T2, ESPI_SYS_INT_T2_SET); + espi_write(priv, ESPI_SYS_IER, ESPI_SYS_INT_SET); + + /* Setup Interrupt Type/Enable of System Event 1 from Master + * T2 T1 T0 + * 1). SUS_WARN : Rising Edge 0 0 1 + */ + espi_write(priv, ESPI_SYS1_INT_T0, ESPI_SYSEVT1_SUS_WARN); + espi_write(priv, ESPI_SYS1_INT_T1, 0x00000000); + espi_write(priv, ESPI_SYS1_INT_T2, 0x00000000); + espi_write(priv, ESPI_SYS1_IER, ESPI_SYSEVT1_SUS_WARN); + + espi_write(priv, ESPI_IER, 0xFFFFFFFF); + + return rc; +} + +static void aspeed_espi_slave_boot_ack(struct aspeed_espi_slave_data *priv) +{ + u32 evt; + + evt = espi_read(priv, ESPI_SYS_EVENT); + if (!(evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS)) + espi_write(priv, ESPI_SYS_EVENT, evt | + ESPI_SYSEVT_SLAVE_BOOT_STATUS | + ESPI_SYSEVT_SLAVE_BOOT_DONE); + + evt = espi_read(priv, ESPI_SYS1_EVENT); + if ((evt & ESPI_SYSEVT1_SUS_WARN) != 0) + espi_write(priv, ESPI_SYS1_EVENT, evt | ESPI_SYSEVT1_SUS_ACK); +} + +static int aspeed_espi_slave_probe(struct platform_device *pdev) +{ + struct aspeed_espi_slave_data *priv; + struct device *dev = &pdev->dev; + struct resource *res; + int rc; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + rc = aspeed_espi_slave_config_irq(priv, pdev); + if (rc) { + dev_err(dev, "Failed to configure IRQ\n"); + return rc; + } + + aspeed_espi_slave_boot_ack(priv); + + platform_set_drvdata(pdev, priv); + + dev_info(dev, "eSPI slave is running!\n"); + + return 0; +} + +static const struct of_device_id of_espi_slave_match_table[] = { + { .compatible = "aspeed,ast2500-espi-slave" }, + {}, +}; + +static struct platform_driver aspeed_espi_slave_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = of_espi_slave_match_table, + }, + .probe = aspeed_espi_slave_probe, +}; + +module_platform_driver(aspeed_espi_slave_driver); + +MODULE_DEVICE_TABLE(of, of_espi_slave_match_table); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Haiyue Wang "); +MODULE_DESCRIPTION("Linux device interface to the eSPI slave");