[PATCH 4/7] soc: aspeed: Add eSPI TAFS backend support
aspeedyh
yh_chung at aspeedtech.com
Fri Mar 13 21:07:39 AEDT 2026
eSPI Target Attached Flash Sharing (TAFS) refers to sharing the storage
mounted on eSPI devices to host via eSPI Flash Channel.
Add support for TAFS storage handling by creating a storage read/write
interface conceptually like Logical Unit Number (LUN) for driver to
route storage requests to the specified TAFS backend.
Signed-off-by: aspeedyh <yh_chung at aspeedtech.com>
---
drivers/soc/aspeed/espi/espi_storage.c | 322 +++++++++++++++++++++++++++++++++
drivers/soc/aspeed/espi/espi_storage.h | 32 ++++
2 files changed, 354 insertions(+)
diff --git a/drivers/soc/aspeed/espi/espi_storage.c b/drivers/soc/aspeed/espi/espi_storage.c
new file mode 100644
index 000000000000..71038e9bdd52
--- /dev/null
+++ b/drivers/soc/aspeed/espi/espi_storage.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * eSPI TAFS back storage interface
+ */
+
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "espi_storage.h"
+
+/*
+ * aspeed_espi_lun: use an existing block device or file as backend storage for eSPI flash channel.
+ *
+ * Typical path:
+ * host <-> eSPI bus <-> eSPI slave protocol
+ * <-> aspeed_espi_lun_*() helpers
+ * <-> backend block device (e.g., /dev/mmcblk0p3)
+ */
+
+int aspeed_espi_lun_open(struct aspeed_espi_lun *lun, const char *path,
+ bool initially_ro, bool cdrom)
+{
+ unsigned int blksize, blkbits;
+ loff_t size, num_sectors;
+ struct inode *inode;
+ struct file *filp;
+ int ro;
+ int rc;
+
+ filp = NULL;
+ ro = initially_ro;
+
+ if (!lun || !path) {
+ pr_err("espi_lun_open: invalid args lun=%p path=%p\n", lun,
+ path);
+ return -EINVAL;
+ }
+
+ pr_info("espi_lun_open: path=%s ro=%d cdrom=%d\n", path, ro, cdrom);
+
+ /* Try R/W first, fallback to R/O if failed */
+ if (!ro) {
+ filp = filp_open(path, O_RDWR | O_LARGEFILE, 0);
+ if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES) {
+ pr_err("espi_lun_open: open rw failed rc=%ld, back to ro\n",
+ PTR_ERR(filp));
+ ro = 1;
+ }
+ }
+
+ if (ro) {
+ filp = filp_open(path, O_RDONLY | O_LARGEFILE, 0);
+ if (IS_ERR_OR_NULL(filp)) {
+ rc = filp ? PTR_ERR(filp) : -ENODEV;
+ pr_err("espi_lun_open: open ro failed rc=%d\n", rc);
+ return rc;
+ }
+ pr_info("espi_lun_open: open ro ok filp=%p\n", filp);
+ }
+
+ if (!(filp->f_mode & FMODE_WRITE))
+ ro = 1;
+
+ pr_info("espi_lun_open: filp=%p f_inode=%p f_mode=0x%x\n", filp,
+ filp ? filp->f_inode : NULL, filp ? filp->f_mode : 0);
+
+ inode = filp->f_mapping->host;
+
+ pr_info("espi_lun_open: inode=%p mode=0%o\n", inode,
+ inode ? inode->i_mode : 0);
+ if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) {
+ rc = -EINVAL;
+ goto out_put;
+ }
+
+ if (!(filp->f_mode & FMODE_CAN_READ)) {
+ rc = -EACCES;
+ goto out_put;
+ }
+ if (!(filp->f_mode & FMODE_CAN_WRITE))
+ ro = true;
+
+ size = i_size_read(inode);
+ if (size < 0) {
+ pr_info("unable to find file size: %s\n", path);
+ rc = (int)size;
+ goto out_put;
+ }
+
+ pr_info("espi_lun_open: size=%lld\n", size);
+
+ if (cdrom) {
+ blksize = 2048;
+ blkbits = 11;
+ } else if (S_ISBLK(inode->i_mode)) {
+ blksize = bdev_logical_block_size(I_BDEV(inode));
+ pr_info("espi_lun_open: blksize=%d\n", blksize);
+ blkbits = blksize_bits(blksize);
+ pr_info("espi_lun_open: bdev=%d\n", blkbits);
+ } else {
+ blksize = 512;
+ blkbits = 9;
+ }
+
+ pr_info("espi_lun_open: blksize=%u blkbits=%u\n", blksize, blkbits);
+ num_sectors = size >> blkbits;
+ if (num_sectors < 1) {
+ pr_info("file too small: %s\n", path);
+ rc = -ETOOSMALL;
+ goto out_put;
+ }
+
+ lun->blksize = blksize;
+ lun->blkbits = blkbits;
+ lun->ro = ro;
+ lun->filp = filp;
+ lun->file_length = size;
+ lun->num_sectors = num_sectors;
+
+ lun->cdrom = cdrom;
+
+ return 0;
+
+out_put:
+ fput(filp);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_open);
+
+void aspeed_espi_lun_close(struct aspeed_espi_lun *lun)
+{
+ if (!lun)
+ return;
+
+ if (lun->filp) {
+ fput(lun->filp);
+ lun->filp = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_close);
+
+int aspeed_espi_lun_rw(struct aspeed_espi_lun *lun, bool write, sector_t sector,
+ unsigned int nsect, void *buf)
+{
+ ssize_t done;
+ loff_t pos;
+ size_t len;
+
+ if (!lun || !lun->filp || !buf)
+ return -EINVAL;
+
+ if (write && lun->ro)
+ return -EROFS;
+
+ if (sector >= lun->num_sectors || nsect > lun->num_sectors - sector)
+ return -EINVAL;
+
+ pos = (loff_t)sector << lun->blkbits;
+ len = (size_t)nsect << lun->blkbits;
+
+ if (write)
+ done = kernel_write(lun->filp, buf, len, &pos);
+ else
+ done = kernel_read(lun->filp, buf, len, &pos);
+
+ if (done != len)
+ return done < 0 ? (int)done : -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_rw);
+
+int aspeed_espi_lun_read(struct aspeed_espi_lun *lun, sector_t sector,
+ unsigned int nsect, void *buf)
+{
+ return aspeed_espi_lun_rw(lun, false, sector, nsect, buf);
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_read);
+
+int aspeed_espi_lun_write(struct aspeed_espi_lun *lun, sector_t sector,
+ unsigned int nsect, void *buf)
+{
+ return aspeed_espi_lun_rw(lun, true, sector, nsect, buf);
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_write);
+
+/*
+ * Byte-granular R/W, for eSPI protocol handler use:
+ * - addr/len are in bytes, allowing arbitrary unaligned access
+ * - internally handles sector alignment and RMW
+ */
+int aspeed_espi_lun_rw_bytes(struct aspeed_espi_lun *lun, bool write, u32 addr,
+ u32 len, u8 *buf)
+{
+ u32 blk_mask;
+ u32 blksize;
+ u8 *bounce;
+ u32 done;
+ int rc;
+
+ done = 0;
+ rc = 0;
+
+ if (!lun || !lun->filp || !buf)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ if (write && lun->ro)
+ return -EROFS;
+
+ blksize = lun->blksize;
+ blk_mask = blksize - 1;
+
+ bounce = kmalloc(blksize, GFP_KERNEL);
+ if (!bounce)
+ return -ENOMEM;
+
+ while (done < len) {
+ u32 cur_addr = addr + done;
+ u32 off_in_blk = cur_addr & blk_mask;
+ sector_t sector = cur_addr >> lun->blkbits;
+ u32 bytes_this;
+ u8 *p = buf + done;
+
+ /* Process up to the end of this sector */
+ bytes_this = blksize - off_in_blk;
+ if (bytes_this > (len - done))
+ bytes_this = len - done;
+
+ if (!off_in_blk && bytes_this == blksize) {
+ /* Fully aligned sector, use sector API directly */
+ if (write)
+ rc = aspeed_espi_lun_write(lun, sector, 1, p);
+ else
+ rc = aspeed_espi_lun_read(lun, sector, 1, p);
+ } else {
+ /* partial sector: read one sector first, then overwrite/extract part */
+ rc = aspeed_espi_lun_read(lun, sector, 1, bounce);
+ if (rc)
+ break;
+
+ if (write) {
+ memcpy(bounce + off_in_blk, p, bytes_this);
+ rc = aspeed_espi_lun_write(lun, sector, 1,
+ bounce);
+ } else {
+ memcpy(p, bounce + off_in_blk, bytes_this);
+ }
+ }
+
+ if (rc)
+ break;
+
+ done += bytes_this;
+ }
+
+ kfree(bounce);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_rw_bytes);
+
+/*
+ * Erase: specify range in bytes, implemented as writing 0xFF pattern.
+ * For eSPI ERASE command use.
+ */
+int aspeed_espi_lun_erase_bytes(struct aspeed_espi_lun *lun, u32 addr, u32 len)
+{
+ u8 *pattern;
+ u32 chunk;
+ u32 done;
+ int rc;
+
+ done = 0;
+ rc = 0;
+
+ if (!lun || !lun->filp)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ if (lun->ro)
+ return -EROFS;
+
+ chunk = lun->blksize;
+ if (chunk > len)
+ chunk = len;
+
+ pattern = kmalloc(chunk, GFP_KERNEL);
+ if (!pattern)
+ return -ENOMEM;
+
+ while (done < len) {
+ u32 this_len = len - done;
+
+ if (this_len > chunk)
+ this_len = chunk;
+
+ memset(pattern, 0xff, this_len);
+
+ rc = aspeed_espi_lun_rw_bytes(lun, true, addr + done, this_len,
+ pattern);
+ if (rc)
+ break;
+
+ done += this_len;
+ }
+
+ kfree(pattern);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(aspeed_espi_lun_erase_bytes);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ASPEED eSPI slave storage backend helpers");
+MODULE_AUTHOR("ASPEED");
diff --git a/drivers/soc/aspeed/espi/espi_storage.h b/drivers/soc/aspeed/espi/espi_storage.h
new file mode 100644
index 000000000000..f0711328b03e
--- /dev/null
+++ b/drivers/soc/aspeed/espi/espi_storage.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef ASPEED_ESPI_STORAGE_H
+#define ASPEED_ESPI_STORAGE_H
+
+#include <linux/fs.h>
+#include <linux/types.h>
+
+struct aspeed_espi_lun {
+ struct file *filp;
+ loff_t file_length;
+ loff_t num_sectors;
+ unsigned int blksize;
+ unsigned int blkbits;
+ bool ro;
+ bool cdrom;
+};
+
+int aspeed_espi_lun_open(struct aspeed_espi_lun *lun, const char *path,
+ bool initially_ro, bool cdrom);
+void aspeed_espi_lun_close(struct aspeed_espi_lun *lun);
+int aspeed_espi_lun_rw(struct aspeed_espi_lun *lun, bool write,
+ sector_t sector, unsigned int nsect, void *buf);
+int aspeed_espi_lun_read(struct aspeed_espi_lun *lun, sector_t sector,
+ unsigned int nsect, void *buf);
+int aspeed_espi_lun_write(struct aspeed_espi_lun *lun, sector_t sector,
+ unsigned int nsect, void *buf);
+int aspeed_espi_lun_rw_bytes(struct aspeed_espi_lun *lun, bool write,
+ u32 addr, u32 len, u8 *buf);
+int aspeed_espi_lun_erase_bytes(struct aspeed_espi_lun *lun,
+ u32 addr, u32 len);
+
+#endif /* _ASPEED_ESPI_STORAGE_H_ */
--
2.34.1
More information about the openbmc
mailing list