From nobody Tue Apr 7 12:52:12 2026 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C92838C430; Fri, 13 Mar 2026 10:08:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773396506; cv=none; b=b119MIphd6Li8tls97ptRtK2rrHyOnaMqbXtgAf6nNIs+rW8HX7+3JSC63yGjQgYWZWv4VXzAr1a5W4mkZIcJvoX61vccbEiRc9O/E28fJbTANoaBQfTzTet0pUER3wYhVbjAqur8G+mrQnZybM7at5tzd++/DUWCvwDT6mZ2Ss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773396506; c=relaxed/simple; bh=/gT8PPPSgtivGqhJGfnX1C479G896inIVlPTIjaL2gM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=XjNZw3ZayXR5sXFH8vXigV3fgul9EN45eeizZUIBBWjr+mxVNUcsC5HijNw6B2caV3+amBIAtqCeLBFXl/QTgZ/1GebPqW34KBS713/GLgGnYLbXP4BxEF5hi5EGVE2r1Sa0QwJPzRNWmuT4IRqc96Rv4F7SY/TugiMU16qVwGI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Fri, 13 Mar 2026 18:08:12 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Fri, 13 Mar 2026 18:08:12 +0800 From: aspeedyh Date: Fri, 13 Mar 2026 18:07:39 +0800 Subject: [PATCH 4/7] soc: aspeed: Add eSPI TAFS backend support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260313-upstream_espi-v1-4-9504428e1f43@aspeedtech.com> References: <20260313-upstream_espi-v1-0-9504428e1f43@aspeedtech.com> In-Reply-To: <20260313-upstream_espi-v1-0-9504428e1f43@aspeedtech.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , "Andrew Jeffery" , Ryan Chen , Philipp Zabel CC: , , , , , , aspeedyh X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773396491; l=9656; i=yh_chung@aspeedtech.com; s=20260313; h=from:subject:message-id; bh=/gT8PPPSgtivGqhJGfnX1C479G896inIVlPTIjaL2gM=; b=pxfbLJ5IQCgYFbhg75xnoNK6S8Q8zbJPNxdm67mqkgFlS/68AfZMbE0wPvAWUgbB5c4zuFNam et7tGN1vxpDA2PCTQqswpq2p1woMCldapD6gBYeT15WXSuBwMFvXtvY X-Developer-Key: i=yh_chung@aspeedtech.com; a=ed25519; pk=o71dz0J8lpN+v0f3Mk4gT9PfVngADPC1Pex4aK6VigM= 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 --- 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/es= pi/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 +#include +#include +#include +#include +#include + +#include "espi_storage.h" + +/* + * aspeed_espi_lun: use an existing block device or file as backend storag= e 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 =3D NULL; + ro =3D initially_ro; + + if (!lun || !path) { + pr_err("espi_lun_open: invalid args lun=3D%p path=3D%p\n", lun, + path); + return -EINVAL; + } + + pr_info("espi_lun_open: path=3D%s ro=3D%d cdrom=3D%d\n", path, ro, cdrom); + + /* Try R/W first, fallback to R/O if failed */ + if (!ro) { + filp =3D filp_open(path, O_RDWR | O_LARGEFILE, 0); + if (PTR_ERR(filp) =3D=3D -EROFS || PTR_ERR(filp) =3D=3D -EACCES) { + pr_err("espi_lun_open: open rw failed rc=3D%ld, back to ro\n", + PTR_ERR(filp)); + ro =3D 1; + } + } + + if (ro) { + filp =3D filp_open(path, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR_OR_NULL(filp)) { + rc =3D filp ? PTR_ERR(filp) : -ENODEV; + pr_err("espi_lun_open: open ro failed rc=3D%d\n", rc); + return rc; + } + pr_info("espi_lun_open: open ro ok filp=3D%p\n", filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro =3D 1; + + pr_info("espi_lun_open: filp=3D%p f_inode=3D%p f_mode=3D0x%x\n", filp, + filp ? filp->f_inode : NULL, filp ? filp->f_mode : 0); + + inode =3D filp->f_mapping->host; + + pr_info("espi_lun_open: inode=3D%p mode=3D0%o\n", inode, + inode ? inode->i_mode : 0); + if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) { + rc =3D -EINVAL; + goto out_put; + } + + if (!(filp->f_mode & FMODE_CAN_READ)) { + rc =3D -EACCES; + goto out_put; + } + if (!(filp->f_mode & FMODE_CAN_WRITE)) + ro =3D true; + + size =3D i_size_read(inode); + if (size < 0) { + pr_info("unable to find file size: %s\n", path); + rc =3D (int)size; + goto out_put; + } + + pr_info("espi_lun_open: size=3D%lld\n", size); + + if (cdrom) { + blksize =3D 2048; + blkbits =3D 11; + } else if (S_ISBLK(inode->i_mode)) { + blksize =3D bdev_logical_block_size(I_BDEV(inode)); + pr_info("espi_lun_open: blksize=3D%d\n", blksize); + blkbits =3D blksize_bits(blksize); + pr_info("espi_lun_open: bdev=3D%d\n", blkbits); + } else { + blksize =3D 512; + blkbits =3D 9; + } + + pr_info("espi_lun_open: blksize=3D%u blkbits=3D%u\n", blksize, blkbits); + num_sectors =3D size >> blkbits; + if (num_sectors < 1) { + pr_info("file too small: %s\n", path); + rc =3D -ETOOSMALL; + goto out_put; + } + + lun->blksize =3D blksize; + lun->blkbits =3D blkbits; + lun->ro =3D ro; + lun->filp =3D filp; + lun->file_length =3D size; + lun->num_sectors =3D num_sectors; + + lun->cdrom =3D 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 =3D NULL; + } +} +EXPORT_SYMBOL_GPL(aspeed_espi_lun_close); + +int aspeed_espi_lun_rw(struct aspeed_espi_lun *lun, bool write, sector_t s= ector, + 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 >=3D lun->num_sectors || nsect > lun->num_sectors - sector) + return -EINVAL; + + pos =3D (loff_t)sector << lun->blkbits; + len =3D (size_t)nsect << lun->blkbits; + + if (write) + done =3D kernel_write(lun->filp, buf, len, &pos); + else + done =3D kernel_read(lun->filp, buf, len, &pos); + + if (done !=3D 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 =3D 0; + rc =3D 0; + + if (!lun || !lun->filp || !buf) + return -EINVAL; + + if (!len) + return 0; + + if (write && lun->ro) + return -EROFS; + + blksize =3D lun->blksize; + blk_mask =3D blksize - 1; + + bounce =3D kmalloc(blksize, GFP_KERNEL); + if (!bounce) + return -ENOMEM; + + while (done < len) { + u32 cur_addr =3D addr + done; + u32 off_in_blk =3D cur_addr & blk_mask; + sector_t sector =3D cur_addr >> lun->blkbits; + u32 bytes_this; + u8 *p =3D buf + done; + + /* Process up to the end of this sector */ + bytes_this =3D blksize - off_in_blk; + if (bytes_this > (len - done)) + bytes_this =3D len - done; + + if (!off_in_blk && bytes_this =3D=3D blksize) { + /* Fully aligned sector, use sector API directly */ + if (write) + rc =3D aspeed_espi_lun_write(lun, sector, 1, p); + else + rc =3D aspeed_espi_lun_read(lun, sector, 1, p); + } else { + /* partial sector: read one sector first, then overwrite/extract part */ + rc =3D aspeed_espi_lun_read(lun, sector, 1, bounce); + if (rc) + break; + + if (write) { + memcpy(bounce + off_in_blk, p, bytes_this); + rc =3D aspeed_espi_lun_write(lun, sector, 1, + bounce); + } else { + memcpy(p, bounce + off_in_blk, bytes_this); + } + } + + if (rc) + break; + + done +=3D 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 =3D 0; + rc =3D 0; + + if (!lun || !lun->filp) + return -EINVAL; + + if (!len) + return 0; + + if (lun->ro) + return -EROFS; + + chunk =3D lun->blksize; + if (chunk > len) + chunk =3D len; + + pattern =3D kmalloc(chunk, GFP_KERNEL); + if (!pattern) + return -ENOMEM; + + while (done < len) { + u32 this_len =3D len - done; + + if (this_len > chunk) + this_len =3D chunk; + + memset(pattern, 0xff, this_len); + + rc =3D aspeed_espi_lun_rw_bytes(lun, true, addr + done, this_len, + pattern); + if (rc) + break; + + done +=3D 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/es= pi/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 +#include + +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_ */ --=20 2.34.1