From: aspeedyh <yh_chung@aspeedtech.com>
To: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>, Joel Stanley <joel@jms.id.au>,
"Andrew Jeffery" <andrew@codeconstruct.com.au>,
Ryan Chen <ryan_chen@aspeedtech.com>,
Philipp Zabel <p.zabel@pengutronix.de>
Cc: <devicetree@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-aspeed@lists.ozlabs.org>, <linux-kernel@vger.kernel.org>,
<openbmc@lists.ozlabs.org>, <maciej.lawniczak@intel.com>,
aspeedyh <yh_chung@aspeedtech.com>
Subject: [PATCH 5/7] soc: aspeed: Add eSPI flash channel support
Date: Fri, 13 Mar 2026 18:07:40 +0800 [thread overview]
Message-ID: <20260313-upstream_espi-v1-5-9504428e1f43@aspeedtech.com> (raw)
In-Reply-To: <20260313-upstream_espi-v1-0-9504428e1f43@aspeedtech.com>
Add flash channel probe/remove and function operators for core to
receive/send eSPI flash request packets. Flash channel packets are
handled in core to address storage requests via the LUN-like interface.
Note eSPI Flash channel may start transaction prior than kernel boots
due to host might accesses BIOS image in early stage. Busy checkings are
added to avoid resetting the Flash channel during probe if transaction
already begun.
Signed-off-by: aspeedyh <yh_chung@aspeedtech.com>
---
drivers/soc/aspeed/espi/Makefile | 2 +-
drivers/soc/aspeed/espi/aspeed-espi-comm.h | 62 ++++++++
drivers/soc/aspeed/espi/aspeed-espi.c | 239 ++++++++++++++++++++++++++++-
drivers/soc/aspeed/espi/aspeed-espi.h | 36 +++++
drivers/soc/aspeed/espi/ast2600-espi.c | 165 ++++++++++++++++++++
drivers/soc/aspeed/espi/ast2600-espi.h | 19 ++-
6 files changed, 515 insertions(+), 8 deletions(-)
diff --git a/drivers/soc/aspeed/espi/Makefile b/drivers/soc/aspeed/espi/Makefile
index 30f9dbf92a0f..44f2adc4d358 100644
--- a/drivers/soc/aspeed/espi/Makefile
+++ b/drivers/soc/aspeed/espi/Makefile
@@ -1 +1 @@
-obj-y += aspeed-espi.o ast2600-espi.o
+obj-y += aspeed-espi.o ast2600-espi.o espi_storage.o
diff --git a/drivers/soc/aspeed/espi/aspeed-espi-comm.h b/drivers/soc/aspeed/espi/aspeed-espi-comm.h
new file mode 100644
index 000000000000..510b4afee82f
--- /dev/null
+++ b/drivers/soc/aspeed/espi/aspeed-espi-comm.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Aspeed eSPI protocol packet definitions
+ * Copyright 2026 Aspeed Technology Inc.
+ */
+#ifndef __ASPEED_ESPI_COMM_H__
+#define __ASPEED_ESPI_COMM_H__
+
+#include <linux/bits.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/*
+ * eSPI cycle type encoding
+ *
+ * Section 5.1 Cycle Types and Packet Format,
+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016.
+ */
+#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
+
+#define ESPI_PLD_LEN_MIN BIT(6)
+#define ESPI_MAX_PLD_LEN BIT(12)
+
+/*
+ * 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 {
+ u8 cyc;
+ u8 len_h : 4;
+ u8 tag : 4;
+ u8 len_l;
+};
+
+struct espi_flash_rwe {
+ u8 cyc;
+ u8 len_h : 4;
+ u8 tag : 4;
+ u8 len_l;
+ u32 addr_be;
+ u8 data[];
+} __packed;
+
+struct espi_flash_cmplt {
+ u8 cyc;
+ u8 len_h : 4;
+ u8 tag : 4;
+ u8 len_l;
+ u8 data[];
+} __packed;
+
+#endif
diff --git a/drivers/soc/aspeed/espi/aspeed-espi.c b/drivers/soc/aspeed/espi/aspeed-espi.c
index e369738119bc..7d58c78ed397 100644
--- a/drivers/soc/aspeed/espi/aspeed-espi.c
+++ b/drivers/soc/aspeed/espi/aspeed-espi.c
@@ -4,6 +4,7 @@
*/
#include <linux/clk.h>
+#include <linux/device/devres.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -13,7 +14,10 @@
#include <linux/reset.h>
#include "aspeed-espi.h"
+#include "aspeed-espi-comm.h"
#include "ast2600-espi.h"
+#include "espi_storage.h"
+
struct aspeed_espi_ops {
void (*espi_pre_init)(struct aspeed_espi *espi);
@@ -21,6 +25,16 @@ struct aspeed_espi_ops {
void (*espi_deinit)(struct aspeed_espi *espi);
int (*espi_perif_probe)(struct aspeed_espi *espi);
int (*espi_perif_remove)(struct aspeed_espi *espi);
+ int (*espi_flash_probe)(struct aspeed_espi *espi);
+ int (*espi_flash_remove)(struct aspeed_espi *espi);
+ int (*espi_flash_get_hdr)(struct aspeed_espi *espi,
+ struct espi_comm_hdr *hdr);
+ int (*espi_flash_get_pkt)(struct aspeed_espi *espi, void *pkt_buf,
+ size_t pkt_size);
+ int (*espi_flash_put_pkt)(struct aspeed_espi *espi,
+ struct espi_flash_cmplt hdr, void *pkt_buf,
+ size_t pkt_size);
+ void (*espi_flash_clr_pkt)(struct aspeed_espi *espi);
irqreturn_t (*espi_isr)(int irq, void *espi);
};
@@ -30,6 +44,12 @@ static const struct aspeed_espi_ops aspeed_espi_ast2600_ops = {
.espi_deinit = ast2600_espi_deinit,
.espi_perif_probe = ast2600_espi_perif_probe,
.espi_perif_remove = ast2600_espi_perif_remove,
+ .espi_flash_probe = ast2600_espi_flash_probe,
+ .espi_flash_remove = ast2600_espi_flash_remove,
+ .espi_flash_get_hdr = ast2600_espi_flash_get_hdr,
+ .espi_flash_get_pkt = ast2600_espi_flash_get_pkt,
+ .espi_flash_put_pkt = ast2600_espi_flash_put_pkt,
+ .espi_flash_clr_pkt = ast2600_espi_flash_clr_pkt,
.espi_isr = ast2600_espi_isr,
};
@@ -39,6 +59,207 @@ static const struct of_device_id aspeed_espi_of_matches[] = {
};
MODULE_DEVICE_TABLE(of, aspeed_espi_of_matches);
+static void aspeed_espi_flash_handle_lun(struct aspeed_espi *espi)
+{
+ u32 cyc, len, tag, pkt_len, addr, offset;
+ struct espi_flash_cmplt resp_pkt;
+ struct aspeed_espi_flash *flash;
+ struct espi_flash_rwe *req_pkt;
+ struct espi_comm_hdr hdr;
+ u8 *payload;
+ u8 *buf;
+ int rc;
+
+ payload = NULL;
+ buf = NULL;
+
+ flash = &espi->flash;
+ if (!flash->lun || !flash->lun->filp)
+ return;
+
+ rc = espi->ops->espi_flash_get_hdr(espi, &hdr);
+ if (rc) {
+ dev_err(espi->dev, "espi_flash_handle_lun: get_hdr failed rc=%d\n", rc);
+ return;
+ }
+
+ if (hdr.cyc != ESPI_FLASH_WRITE && hdr.cyc != ESPI_FLASH_READ &&
+ hdr.cyc != ESPI_FLASH_ERASE) {
+ dev_err(espi->dev, "espi_flash_handle_lun: invalid cyc=0x%x\n",
+ hdr.cyc);
+ return;
+ }
+
+ cyc = hdr.cyc;
+ len = (hdr.len_h << 8) | hdr.len_l;
+ tag = hdr.tag;
+
+ len = len ? len : ESPI_MAX_PLD_LEN;
+ pkt_len = len + sizeof(struct espi_flash_rwe);
+
+ payload = kzalloc(pkt_len, GFP_KERNEL);
+ if (!payload)
+ return;
+
+ rc = espi->ops->espi_flash_get_pkt(espi, payload + sizeof(hdr), pkt_len - sizeof(hdr));
+ if (rc) {
+ dev_err(espi->dev, "espi_flash_handle_lun: get_pkt failed rc=%d\n", rc);
+ goto out_free;
+ }
+
+ req_pkt = (struct espi_flash_rwe *)payload;
+ req_pkt->cyc = hdr.cyc;
+ req_pkt->len_h = hdr.len_h;
+ req_pkt->len_l = hdr.len_l;
+ req_pkt->tag = hdr.tag;
+
+ addr = be32_to_cpu(req_pkt->addr_be);
+
+ switch (cyc) {
+ case ESPI_FLASH_ERASE:
+ rc = aspeed_espi_lun_erase_bytes(flash->lun, addr, len);
+ resp_pkt.cyc = (rc) ? ESPI_FLASH_UNSUC_CMPLT : ESPI_FLASH_SUC_CMPLT;
+ resp_pkt.len_h = 0;
+ resp_pkt.len_l = 0;
+ resp_pkt.tag = tag;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt, NULL, 0);
+ break;
+ case ESPI_FLASH_WRITE:
+ rc = aspeed_espi_lun_rw_bytes(flash->lun, true, addr, len,
+ &payload[sizeof(struct espi_flash_rwe)]);
+
+ resp_pkt.cyc = (rc) ? ESPI_FLASH_UNSUC_CMPLT : ESPI_FLASH_SUC_CMPLT;
+ resp_pkt.len_h = 0;
+ resp_pkt.len_l = 0;
+ resp_pkt.tag = tag;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt, NULL, 0);
+ break;
+ case ESPI_FLASH_READ:
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ goto out_free;
+
+ rc = aspeed_espi_lun_rw_bytes(flash->lun, false, addr, len, buf);
+ if (rc) {
+ resp_pkt.cyc = ESPI_FLASH_UNSUC_CMPLT;
+ resp_pkt.len_h = 0;
+ resp_pkt.len_l = 0;
+ resp_pkt.tag = tag;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt, NULL, 0);
+ } else {
+ if (len <= ESPI_PLD_LEN_MIN) {
+ resp_pkt.cyc = ESPI_FLASH_SUC_CMPLT_D_ONLY;
+ resp_pkt.tag = tag;
+ resp_pkt.len_h = (len >> 8) & 0xff;
+ resp_pkt.len_l = len & 0xff;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt, buf, len);
+ } else {
+ resp_pkt.cyc = ESPI_FLASH_SUC_CMPLT_D_FIRST;
+ resp_pkt.tag = tag;
+ resp_pkt.len_h = (ESPI_PLD_LEN_MIN >> 8) & 0xff;
+ resp_pkt.len_l = ESPI_PLD_LEN_MIN & 0xff;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt, buf,
+ ESPI_PLD_LEN_MIN);
+ offset = ESPI_PLD_LEN_MIN;
+ len -= ESPI_PLD_LEN_MIN;
+
+ while (len > ESPI_PLD_LEN_MIN) {
+ resp_pkt.cyc = ESPI_FLASH_SUC_CMPLT_D_MIDDLE;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt,
+ &buf[offset],
+ ESPI_PLD_LEN_MIN);
+ offset += ESPI_PLD_LEN_MIN;
+ len -= ESPI_PLD_LEN_MIN;
+ }
+
+ resp_pkt.cyc = ESPI_FLASH_SUC_CMPLT_D_LAST;
+ resp_pkt.len_h = (len >> 8) & 0xff;
+ resp_pkt.len_l = len & 0xff;
+ espi->ops->espi_flash_put_pkt(espi, resp_pkt,
+ &buf[offset], len);
+ }
+ }
+ break;
+ default:
+ dev_err(espi->dev, "espi_flash_handle_lun: unsupported cyc=0x%x\n", cyc);
+ break;
+ }
+ espi->ops->espi_flash_clr_pkt(espi);
+out_free:
+ kfree(buf);
+ kfree(payload);
+}
+
+static void aspeed_espi_flash_rx_work(struct work_struct *work)
+{
+ struct aspeed_espi_flash *flash = container_of(work, struct aspeed_espi_flash, rx_work);
+ struct aspeed_espi *espi = container_of(flash, struct aspeed_espi, flash);
+
+ mutex_lock(&flash->tx_mtx);
+ aspeed_espi_flash_handle_lun(espi);
+ mutex_unlock(&flash->tx_mtx);
+}
+
+static int aspeed_espi_flash_probe(struct aspeed_espi *espi)
+{
+ struct aspeed_espi_flash *flash;
+ struct device *dev;
+
+ flash = &espi->flash;
+ dev = espi->dev;
+
+ flash->dma.enable = of_property_read_bool(dev->of_node, "aspeed,flash-dma-mode");
+ if (flash->dma.enable) {
+ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr,
+ GFP_KERNEL);
+ if (!flash->dma.tx_virt) {
+ dev_err(dev, "cannot allocate DMA TX buffer\n");
+ return -ENOMEM;
+ }
+
+ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr,
+ GFP_KERNEL);
+ if (!flash->dma.rx_virt) {
+ dev_err(dev, "cannot allocate DMA RX buffer\n");
+ return -ENOMEM;
+ }
+ }
+
+ mutex_init(&flash->tx_mtx);
+ INIT_WORK(&flash->rx_work, aspeed_espi_flash_rx_work);
+
+ mutex_init(&espi->flash.lun_mtx);
+ espi->flash.lun = NULL;
+ espi->flash.lun_path[0] = '\0';
+ espi->flash.lun_ro = false;
+
+ return espi->ops->espi_flash_probe(espi);
+}
+
+static void aspeed_espi_flash_remove(struct aspeed_espi *espi)
+{
+ struct aspeed_espi_flash *flash;
+
+ flash = &espi->flash;
+
+ if (espi->ops->espi_flash_remove)
+ espi->ops->espi_flash_remove(espi);
+
+ cancel_work_sync(&flash->rx_work);
+
+ if (flash->dma.enable) {
+ dmam_free_coherent(espi->dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr);
+ dmam_free_coherent(espi->dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr);
+ }
+
+ mutex_destroy(&flash->lun_mtx);
+ mutex_destroy(&flash->tx_mtx);
+
+ flash->lun = NULL;
+ flash->lun_path[0] = '\0';
+ flash->lun_ro = false;
+}
+
static int aspeed_espi_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -109,11 +330,17 @@ static int aspeed_espi_probe(struct platform_device *pdev)
}
}
+ rc = aspeed_espi_flash_probe(espi);
+ if (rc) {
+ dev_err(dev, "cannot init flash channel, rc=%d\n", rc);
+ goto err_remove_perif;
+ }
+
rc = devm_request_irq(dev, espi->irq, espi->ops->espi_isr, 0,
dev_name(dev), espi);
if (rc) {
dev_err(dev, "cannot request IRQ\n");
- goto err_deinit;
+ goto err_remove_flash;
}
if (espi->ops->espi_post_init)
@@ -125,12 +352,16 @@ static int aspeed_espi_probe(struct platform_device *pdev)
return 0;
+err_remove_flash:
+ aspeed_espi_flash_remove(espi);
+err_remove_perif:
+ if (espi->ops->espi_perif_remove)
+ espi->ops->espi_perif_remove(espi);
err_deinit:
if (espi->ops->espi_deinit)
espi->ops->espi_deinit(espi);
clk_disable_unprepare(espi->clk);
-
- return rc;
+ return dev_err_probe(dev, rc, "%s failed\n", __func__);
}
static void aspeed_espi_remove(struct platform_device *pdev)
@@ -142,6 +373,8 @@ static void aspeed_espi_remove(struct platform_device *pdev)
if (!espi)
return;
+ aspeed_espi_flash_remove(espi);
+
if (espi->ops->espi_perif_remove)
espi->ops->espi_perif_remove(espi);
diff --git a/drivers/soc/aspeed/espi/aspeed-espi.h b/drivers/soc/aspeed/espi/aspeed-espi.h
index f4ad7f61fef6..7598bc622b95 100644
--- a/drivers/soc/aspeed/espi/aspeed-espi.h
+++ b/drivers/soc/aspeed/espi/aspeed-espi.h
@@ -9,9 +9,44 @@
#include <linux/irqreturn.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
#include <linux/types.h>
+#include <linux/workqueue.h>
#define DEVICE_NAME "aspeed-espi"
+#define ASPEED_ESPI_LUN_PATH_MAX 256
+
+enum aspeed_tafs_mode {
+ TAFS_MODE_SW = 1,
+};
+
+struct aspeed_espi_lun;
+
+struct aspeed_espi_flash {
+ struct {
+ enum aspeed_tafs_mode mode;
+ phys_addr_t taddr;
+ resource_size_t size;
+ } tafs;
+
+ struct {
+ bool enable;
+ void *tx_virt;
+ dma_addr_t tx_addr;
+ void *rx_virt;
+ dma_addr_t rx_addr;
+ } dma;
+
+ struct mutex tx_mtx; /* protects tx virt/addr */
+
+ struct work_struct rx_work;
+
+ struct mutex lun_mtx; /* protects lun metadata r/w */
+ struct aspeed_espi_lun *lun;
+ char lun_path[ASPEED_ESPI_LUN_PATH_MAX];
+ bool lun_ro;
+};
struct aspeed_espi {
struct platform_device *pdev;
@@ -21,6 +56,7 @@ struct aspeed_espi {
struct clk *clk;
int dev_id;
int irq;
+ struct aspeed_espi_flash flash;
const struct aspeed_espi_ops *ops;
};
diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/ast2600-espi.c
index 8effd0404d1f..c3ea01866b45 100644
--- a/drivers/soc/aspeed/espi/ast2600-espi.c
+++ b/drivers/soc/aspeed/espi/ast2600-espi.c
@@ -7,6 +7,7 @@
#include <linux/reset.h>
#include "aspeed-espi.h"
+#include "aspeed-espi-comm.h"
#include "ast2600-espi.h"
static void ast2600_espi_perif_isr(struct aspeed_espi *espi)
@@ -93,6 +94,166 @@ int ast2600_espi_perif_remove(struct aspeed_espi *espi)
return 0;
}
+static void ast2600_espi_flash_isr(struct aspeed_espi *espi)
+{
+ struct aspeed_espi_flash *flash;
+ u32 sts;
+
+ flash = &espi->flash;
+
+ sts = readl(espi->regs + ESPI_INT_STS);
+
+ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) {
+ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS);
+ queue_work(system_wq, &flash->rx_work);
+ }
+}
+
+static void ast2600_espi_flash_reset(struct aspeed_espi *espi)
+{
+ struct aspeed_espi_flash *flash;
+ u32 reg;
+
+ flash = &espi->flash;
+
+ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR);
+ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS);
+
+ reg = readl(espi->regs + ESPI_CTRL);
+ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST
+ | ESPI_CTRL_FLASH_RX_SW_RST
+ | ESPI_CTRL_FLASH_TX_DMA_EN
+ | ESPI_CTRL_FLASH_RX_DMA_EN
+ | ESPI_CTRL_FLASH_SW_RDY);
+ writel(reg, espi->regs + ESPI_CTRL);
+
+ udelay(1);
+
+ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST);
+ writel(reg, espi->regs + ESPI_CTRL);
+
+ flash->tafs.mode = TAFS_MODE_SW;
+ reg = readl(espi->regs + ESPI_CTRL) & ~ESPI_CTRL_FLASH_TAFS_MODE;
+ reg |= FIELD_PREP(ESPI_CTRL_FLASH_TAFS_MODE, flash->tafs.mode);
+ writel(reg, espi->regs + ESPI_CTRL);
+
+ if (flash->dma.enable) {
+ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA);
+ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA);
+
+ reg = readl(espi->regs + ESPI_CTRL)
+ | ESPI_CTRL_FLASH_TX_DMA_EN
+ | ESPI_CTRL_FLASH_RX_DMA_EN;
+ writel(reg, espi->regs + ESPI_CTRL);
+ }
+
+ writel(ESPI_INT_EN_FLASH_RX_CMPLT, espi->regs + ESPI_INT_EN);
+
+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY;
+ writel(reg, espi->regs + ESPI_CTRL);
+}
+
+int ast2600_espi_flash_probe(struct aspeed_espi *espi)
+{
+ u32 regs;
+
+ regs = readl(espi->regs + ESPI_STS);
+ if (regs & (ESPI_STS_FLASH_TX_BUSY | ESPI_STS_FLASH_RX_BUSY)) {
+ dev_warn(espi->dev, "eSPI flash channel is busy, deferring...\n");
+ return -EPROBE_DEFER;
+ }
+
+ ast2600_espi_flash_reset(espi);
+ return 0;
+}
+
+int ast2600_espi_flash_remove(struct aspeed_espi *espi)
+{
+ struct aspeed_espi_flash *flash;
+ u32 reg;
+
+ flash = &espi->flash;
+
+ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR);
+
+ reg = readl(espi->regs + ESPI_CTRL);
+ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN
+ | ESPI_CTRL_FLASH_RX_DMA_EN
+ | ESPI_CTRL_FLASH_SW_RDY);
+ writel(reg, espi->regs + ESPI_CTRL);
+
+ return 0;
+}
+
+int ast2600_espi_flash_get_hdr(struct aspeed_espi *espi,
+ struct espi_comm_hdr *hdr)
+{
+ u32 reg, len;
+
+ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL);
+ hdr->cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg);
+ hdr->tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg);
+ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg);
+ hdr->len_h = (len >> 8) & 0xff;
+ hdr->len_l = len & 0xff;
+
+ return 0;
+}
+
+int ast2600_espi_flash_get_pkt(struct aspeed_espi *espi, void *pkt_buf,
+ size_t pkt_size)
+{
+ u32 i;
+ u8 *pkt;
+
+ pkt = (u8 *)pkt_buf;
+
+ if (espi->flash.dma.enable) {
+ memcpy(pkt, espi->flash.dma.rx_virt, pkt_size);
+ } else {
+ for (i = 0; i < pkt_size; ++i)
+ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff;
+ }
+
+ return 0;
+}
+
+int ast2600_espi_flash_put_pkt(struct aspeed_espi *espi,
+ struct espi_flash_cmplt hdr, void *pkt_buf,
+ size_t pkt_size)
+{
+ u32 i, cyc, tag, len, reg;
+ u8 *pkt;
+
+ pkt = (u8 *)pkt_buf;
+
+ if (pkt_buf && pkt_size > 0) {
+ if (espi->flash.dma.enable) {
+ memcpy(espi->flash.dma.tx_virt, pkt, pkt_size);
+ dma_wmb();
+ } else {
+ for (i = 0; i < pkt_size; ++i)
+ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA);
+ }
+ }
+
+ cyc = hdr.cyc;
+ tag = hdr.tag;
+ len = (hdr.len_h << 8) | hdr.len_l;
+ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) |
+ FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) |
+ FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) |
+ ESPI_FLASH_TX_CTRL_TRIG_PEND;
+ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL);
+
+ return 0;
+}
+
+void ast2600_espi_flash_clr_pkt(struct aspeed_espi *espi)
+{
+ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL);
+}
+
/* global control */
irqreturn_t ast2600_espi_isr(int irq, void *arg)
{
@@ -108,6 +269,9 @@ irqreturn_t ast2600_espi_isr(int irq, void *arg)
if (sts & ESPI_INT_STS_PERIF)
ast2600_espi_perif_isr(espi);
+ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT)
+ ast2600_espi_flash_isr(espi);
+
if (sts & ESPI_INT_STS_RST_DEASSERT) {
/* this will clear all interrupt enable and status */
reset_control_assert(espi->rst);
@@ -115,6 +279,7 @@ irqreturn_t ast2600_espi_isr(int irq, void *arg)
ast2600_espi_perif_sw_reset(espi);
ast2600_espi_perif_reset(espi);
+ ast2600_espi_flash_reset(espi);
/* re-enable eSPI_RESET# interrupt */
writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN);
diff --git a/drivers/soc/aspeed/espi/ast2600-espi.h b/drivers/soc/aspeed/espi/ast2600-espi.h
index 309479ee1187..251999dba73f 100644
--- a/drivers/soc/aspeed/espi/ast2600-espi.h
+++ b/drivers/soc/aspeed/espi/ast2600-espi.h
@@ -9,6 +9,7 @@
#include <linux/bits.h>
#include <linux/irqreturn.h>
#include "aspeed-espi.h"
+#include "aspeed-espi-comm.h"
/* registers */
#define ESPI_CTRL 0x000
@@ -27,13 +28,15 @@
#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_EDAF_MODE GENMASK(11, 10)
+#define ESPI_CTRL_FLASH_TAFS_MODE GENMASK(11, 10)
#define ESPI_CTRL_VW_GPIO_SW BIT(9)
#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_STS_FLASH_TX_BUSY BIT(23)
+#define ESPI_STS_FLASH_RX_BUSY BIT(22)
#define ESPI_INT_STS 0x008
#define ESPI_INT_STS_RST_DEASSERT BIT(31)
#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23)
@@ -147,9 +150,9 @@
#define ESPI_PERIF_MMBI_TADDR ESPI_PERIF_MCYC_TADDR
#define ESPI_PERIF_MCYC_MASK 0x08c
#define ESPI_PERIF_MMBI_MASK ESPI_PERIF_MCYC_MASK
-#define ESPI_FLASH_EDAF_TADDR 0x090
-#define ESPI_FLASH_EDAF_TADDR_BASE GENMASK(31, 24)
-#define ESPI_FLASH_EDAF_TADDR_MASK GENMASK(15, 8)
+#define ESPI_FLASH_TAFS_TADDR 0x090
+#define ESPI_FLASH_TAFS_TADDR_BASE GENMASK(31, 24)
+#define ESPI_FLASH_TAFS_TADDR_MASK GENMASK(15, 8)
#define ESPI_VW_SYSEVT_INT_EN 0x094
#define ESPI_VW_SYSEVT 0x098
#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27)
@@ -287,5 +290,13 @@ int ast2600_espi_oob_probe(struct aspeed_espi *espi);
int ast2600_espi_oob_remove(struct aspeed_espi *espi);
int ast2600_espi_flash_probe(struct aspeed_espi *espi);
int ast2600_espi_flash_remove(struct aspeed_espi *espi);
+int ast2600_espi_flash_get_hdr(struct aspeed_espi *espi,
+ struct espi_comm_hdr *hdr);
+int ast2600_espi_flash_get_pkt(struct aspeed_espi *espi, void *pkt_buf,
+ size_t pkt_size);
+int ast2600_espi_flash_put_pkt(struct aspeed_espi *espi,
+ struct espi_flash_cmplt hdr, void *pkt_buf,
+ size_t pkt_size);
+void ast2600_espi_flash_clr_pkt(struct aspeed_espi *espi);
irqreturn_t ast2600_espi_isr(int irq, void *arg);
#endif
--
2.34.1
next prev parent reply other threads:[~2026-03-13 10:08 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-13 10:07 [PATCH 0/7] soc: aspeed: Add AST2600 eSPI controller support aspeedyh
2026-03-13 10:07 ` [PATCH 1/7] dt-bindings: soc: aspeed: Add AST2600 eSPI controller aspeedyh
2026-03-16 7:07 ` Krzysztof Kozlowski
2026-03-16 8:17 ` YH Chung
2026-03-16 11:04 ` Conor Dooley
2026-03-17 8:43 ` YH Chung
2026-03-13 10:07 ` [PATCH 2/7] soc: aspeed: Introduce core eSPI controller support aspeedyh
2026-03-16 9:57 ` Philipp Zabel
2026-03-17 8:40 ` YH Chung
2026-03-13 10:07 ` [PATCH 3/7] soc: aspeed: Add AST2600 peripheral channel port I/O support aspeedyh
2026-03-13 10:07 ` [PATCH 4/7] soc: aspeed: Add eSPI TAFS backend support aspeedyh
2026-03-13 10:07 ` aspeedyh [this message]
2026-03-19 23:53 ` [PATCH 5/7] soc: aspeed: Add eSPI flash channel support kernel test robot
2026-03-20 1:17 ` kernel test robot
2026-03-20 4:19 ` kernel test robot
2026-03-13 10:07 ` [PATCH 6/7] soc: aspeed: Add sysfs controls for flash backend selection aspeedyh
2026-03-13 10:07 ` [PATCH 7/7] arm: dts: aspeed: Add eSPI node for AST2600 aspeedyh
2026-03-13 16:24 ` [PATCH 0/7] soc: aspeed: Add AST2600 eSPI controller support Conor Dooley
2026-03-13 16:32 ` Mark Brown
2026-03-13 16:48 ` Mark Brown
2026-03-16 3:07 ` YH Chung
2026-03-13 21:36 ` Arnd Bergmann
2026-03-14 1:02 ` Mark Brown
2026-03-16 6:06 ` Ivan Mikhaylov
2026-03-16 6:34 ` Andrew Jeffery
2026-03-17 8:14 ` YH Chung
2026-03-17 9:50 ` Arnd Bergmann
2026-03-25 8:41 ` YH Chung
2026-03-25 10:30 ` Arnd Bergmann
2026-03-27 4:14 ` YH Chung
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260313-upstream_espi-v1-5-9504428e1f43@aspeedtech.com \
--to=yh_chung@aspeedtech.com \
--cc=andrew@codeconstruct.com.au \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=joel@jms.id.au \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-aspeed@lists.ozlabs.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maciej.lawniczak@intel.com \
--cc=openbmc@lists.ozlabs.org \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=ryan_chen@aspeedtech.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox