Add MMIO accessors to support big-endian memory operations. These helpers
include {read, write}{w, l, q}_be() and {read, write}s{w, l, q}_be(),
which allows to access big-endian memory regions while returning
the results in the CPU’s native endianness.
This provides a consistent interface to interact with hardware using
big-endian register layouts.
Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
Changes since V7:
This patch introduced in V7.
Changes for V8:
None.
---
include/asm-generic/io.h | 202 +++++++++++++++++++++++++++++++++++++++
1 file changed, 202 insertions(+)
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 11abad6c87e1..d18a8ca6c06c 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -295,6 +295,96 @@ static inline void writeq(u64 value, volatile void __iomem *addr)
#endif
#endif /* CONFIG_64BIT */
+/*
+ * {read,write}{w,l,q}_be() access big endian memory and return result
+ * in native endianness.
+ */
+
+#ifndef readw_be
+#define readw_be readw_be
+static inline u16 readw_be(const volatile void __iomem *addr)
+{
+ u16 val;
+
+ log_read_mmio(16, addr, _THIS_IP_, _RET_IP_);
+ __io_br();
+ val = __be16_to_cpu((__be16 __force)__raw_readw(addr));
+ __io_ar(val);
+ log_post_read_mmio(val, 16, addr, _THIS_IP_, _RET_IP_);
+ return val;
+}
+#endif
+
+#ifndef readl_be
+#define readl_be readl_be
+static inline u32 readl_be(const volatile void __iomem *addr)
+{
+ u32 val;
+
+ log_read_mmio(32, addr, _THIS_IP_, _RET_IP_);
+ __io_br();
+ val = __be32_to_cpu((__be32 __force)__raw_readl(addr));
+ __io_ar(val);
+ log_post_read_mmio(val, 32, addr, _THIS_IP_, _RET_IP_);
+ return val;
+}
+#endif
+
+#ifdef CONFIG_64BIT
+#ifndef readq_be
+#define readq_be readq_be
+static inline u64 readq_be(const volatile void __iomem *addr)
+{
+ u64 val;
+
+ log_read_mmio(64, addr, _THIS_IP_, _RET_IP_);
+ __io_br();
+ val = __be64_to_cpu((__be64 __force)__raw_readq(addr));
+ __io_ar(val);
+ log_post_read_mmio(val, 64, addr, _THIS_IP_, _RET_IP_);
+ return val;
+}
+#endif
+#endif /* CONFIG_64BIT */
+
+#ifndef writew_be
+#define writew_be writew_be
+static inline void writew_be(u16 value, volatile void __iomem *addr)
+{
+ log_write_mmio(value, 16, addr, _THIS_IP_, _RET_IP_);
+ __io_bw();
+ __raw_writew((u16 __force)__cpu_to_be16(value), addr);
+ __io_aw();
+ log_post_write_mmio(value, 16, addr, _THIS_IP_, _RET_IP_);
+}
+#endif
+
+#ifndef writel_be
+#define writel_be writel_be
+static inline void writel_be(u32 value, volatile void __iomem *addr)
+{
+ log_write_mmio(value, 32, addr, _THIS_IP_, _RET_IP_);
+ __io_bw();
+ __raw_writel((u32 __force)__cpu_to_be32(value), addr);
+ __io_aw();
+ log_post_write_mmio(value, 32, addr, _THIS_IP_, _RET_IP_);
+}
+#endif
+
+#ifdef CONFIG_64BIT
+#ifndef writeq_be
+#define writeq_be writeq_be
+static inline void writeq_be(u64 value, volatile void __iomem *addr)
+{
+ log_write_mmio(value, 64, addr, _THIS_IP_, _RET_IP_);
+ __io_bw();
+ __raw_writeq((u64 __force)__cpu_to_be64(value), addr);
+ __io_aw();
+ log_post_write_mmio(value, 64, addr, _THIS_IP_, _RET_IP_);
+}
+#endif
+#endif /* CONFIG_64BIT */
+
/*
* {read,write}{b,w,l,q}_relaxed() are like the regular version, but
* are not guaranteed to provide ordering against spinlocks or memory
@@ -524,6 +614,118 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
#endif
#endif /* CONFIG_64BIT */
+/*
+ * {read,write}s{w,l,q}_be() repeatedly access the same memory address
+ * in big endianness in 16-, 32- or 64-bit chunks (@count times) and
+ * return result in native endianness.
+ */
+
+#ifndef readsw_be
+#define readsw_be readsw_be
+static inline void readsw_be(const volatile void __iomem *addr,
+ void *buffer,
+ unsigned int count)
+{
+ if (count) {
+ u16 *buf = buffer;
+
+ do {
+ u16 x = __be16_to_cpu((__be16 __force)__raw_readw(addr));
+ *buf++ = x;
+ } while (--count);
+ }
+}
+#endif
+
+#ifndef readsl_be
+#define readsl_be readsl_be
+static inline void readsl_be(const volatile void __iomem *addr,
+ void *buffer,
+ unsigned int count)
+{
+ if (count) {
+ u32 *buf = buffer;
+
+ do {
+ u32 x = __be32_to_cpu((__be32 __force)__raw_readl(addr));
+ *buf++ = x;
+ } while (--count);
+ }
+}
+#endif
+
+#ifdef CONFIG_64BIT
+#ifndef readsq_be
+#define readsq_be readsq_be
+static inline void readsq_be(const volatile void __iomem *addr,
+ void *buffer,
+ unsigned int count)
+{
+ if (count) {
+ u64 *buf = buffer;
+
+ do {
+ u64 x = __be64_to_cpu((__be64 __force)__raw_readq(addr));
+ *buf++ = x;
+ } while (--count);
+ }
+}
+#endif
+#endif /* CONFIG_64BIT */
+
+#ifndef writesw_be
+#define writesw_be writesw_be
+static inline void writesw_be(volatile void __iomem *addr,
+ const void *buffer,
+ unsigned int count)
+{
+ if (count) {
+ const u16 *buf = buffer;
+
+ do {
+ __raw_writew((u16 __force)__cpu_to_be16(*buf), addr);
+ buf++;
+ } while (--count);
+ }
+}
+#endif
+
+#ifndef writesl_be
+#define writesl_be writesl_be
+static inline void writesl_be(volatile void __iomem *addr,
+ const void *buffer,
+ unsigned int count)
+{
+ if (count) {
+ const u32 *buf = buffer;
+
+ do {
+ __raw_writel((u32 __force)__cpu_to_be32(*buf), addr);
+ buf++;
+ } while (--count);
+ }
+}
+#endif
+
+#ifdef CONFIG_64BIT
+#ifndef writesq_be
+#define writesq_be writesq_be
+static inline void writesq_be(volatile void __iomem *addr,
+ const void *buffer,
+ unsigned int count)
+{
+ if (count) {
+ const u64 *buf = buffer;
+
+ do {
+ __raw_writeq((u64 __force)__cpu_to_be64(*buf), addr);
+ buf++;
+ } while (--count);
+ }
+}
+#endif
+#endif /* CONFIG_64BIT */
+
#ifndef PCI_IOBASE
#define PCI_IOBASE ((void __iomem *)0)
#endif
--
2.34.1
Hi Manikanta, kernel test robot noticed the following build warnings: [auto build test WARNING on robh/for-next] [also build test WARNING on arnd-asm-generic/master linus/master v6.17-rc7 next-20250926] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Manikanta-Guntupalli/dt-bindings-i3c-Add-AMD-I3C-master-controller-support/20250926-190033 base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next patch link: https://lore.kernel.org/r/20250926105349.2932952-3-manikanta.guntupalli%40amd.com patch subject: [PATCH V8 2/5] asm-generic/io.h: Add big-endian MMIO accessors config: sparc64-defconfig (https://download.01.org/0day-ci/archive/20250927/202509271308.NHfa8qUq-lkp@intel.com/config) compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250927/202509271308.NHfa8qUq-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202509271308.NHfa8qUq-lkp@intel.com/ All warnings (new ones prefixed by >>): include/uapi/linux/swab.h:19:12: note: expanded from macro '___constant_swab32' 19 | (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: In file included from arch/sparc/include/asm/io.h:7: In file included from arch/sparc/include/asm/io_32.h:21: include/asm-generic/io.h:787:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 787 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/big_endian.h:35:59: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) | ^ include/uapi/linux/swab.h:119:21: note: expanded from macro '__swab32' 119 | ___constant_swab32(x) : \ | ^ include/uapi/linux/swab.h:20:12: note: expanded from macro '___constant_swab32' 20 | (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: In file included from arch/sparc/include/asm/io.h:7: In file included from arch/sparc/include/asm/io_32.h:21: include/asm-generic/io.h:787:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 787 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/big_endian.h:35:59: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) | ^ include/uapi/linux/swab.h:119:21: note: expanded from macro '__swab32' 119 | ___constant_swab32(x) : \ | ^ include/uapi/linux/swab.h:21:12: note: expanded from macro '___constant_swab32' 21 | (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: In file included from arch/sparc/include/asm/io.h:7: In file included from arch/sparc/include/asm/io_32.h:21: include/asm-generic/io.h:787:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 787 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/big_endian.h:35:59: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) | ^ include/uapi/linux/swab.h:119:21: note: expanded from macro '__swab32' 119 | ___constant_swab32(x) : \ | ^ include/uapi/linux/swab.h:22:12: note: expanded from macro '___constant_swab32' 22 | (((__u32)(x) & (__u32)0xff000000UL) >> 24))) | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: In file included from arch/sparc/include/asm/io.h:7: In file included from arch/sparc/include/asm/io_32.h:21: include/asm-generic/io.h:787:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 787 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/big_endian.h:35:59: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) | ^ include/uapi/linux/swab.h:120:12: note: expanded from macro '__swab32' 120 | __fswab32(x)) | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: In file included from arch/sparc/include/asm/io.h:7: In file included from arch/sparc/include/asm/io_32.h:21: include/asm-generic/io.h:803:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 803 | __raw_writeb(value, PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:818:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 818 | __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:833:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 833 | __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:926:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 926 | readsb(PCI_IOBASE + addr, buffer, count); | ~~~~~~~~~~ ^ include/asm-generic/io.h:939:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 939 | readsw(PCI_IOBASE + addr, buffer, count); | ~~~~~~~~~~ ^ include/asm-generic/io.h:952:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 952 | readsl(PCI_IOBASE + addr, buffer, count); | ~~~~~~~~~~ ^ include/asm-generic/io.h:966:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 966 | writesb(PCI_IOBASE + addr, buffer, count); | ~~~~~~~~~~ ^ include/asm-generic/io.h:980:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 980 | writesw(PCI_IOBASE + addr, buffer, count); | ~~~~~~~~~~ ^ include/asm-generic/io.h:994:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 994 | writesl(PCI_IOBASE + addr, buffer, count); | ~~~~~~~~~~ ^ include/asm-generic/io.h:1377:55: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 1377 | return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port; | ~~~~~~~~~~ ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: >> arch/sparc/include/asm/io.h:16:9: warning: 'readw_be' macro redefined [-Wmacro-redefined] 16 | #define readw_be(__addr) __raw_readw(__addr) | ^ include/asm-generic/io.h:304:9: note: previous definition is here 304 | #define readw_be readw_be | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: >> arch/sparc/include/asm/io.h:17:9: warning: 'readl_be' macro redefined [-Wmacro-redefined] 17 | #define readl_be(__addr) __raw_readl(__addr) | ^ include/asm-generic/io.h:319:9: note: previous definition is here 319 | #define readl_be readl_be | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: >> arch/sparc/include/asm/io.h:19:9: warning: 'writel_be' macro redefined [-Wmacro-redefined] 19 | #define writel_be(__w, __addr) __raw_writel(__w, __addr) | ^ include/asm-generic/io.h:363:9: note: previous definition is here 363 | #define writel_be writel_be | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: In file included from arch/sparc/vdso/vdso32/../vclock_gettime.c:18: >> arch/sparc/include/asm/io.h:20:9: warning: 'writew_be' macro redefined [-Wmacro-redefined] 20 | #define writew_be(__l, __addr) __raw_writew(__l, __addr) | ^ include/asm-generic/io.h:351:9: note: previous definition is here 351 | #define writew_be writew_be | ^ In file included from arch/sparc/vdso/vdso32/vclock_gettime.c:22: arch/sparc/vdso/vdso32/../vclock_gettime.c:274:1: warning: no previous prototype for function '__vdso_clock_gettime' [-Wmissing-prototypes] 274 | __vdso_clock_gettime(clockid_t clock, struct __kernel_old_timespec *ts) | ^ arch/sparc/vdso/vdso32/../vclock_gettime.c:273:9: note: declare 'static' if the function is not intended to be used outside of this translation unit 273 | notrace int | ^ | static arch/sparc/vdso/vdso32/../vclock_gettime.c:302:1: warning: no previous prototype for function '__vdso_clock_gettime_stick' [-Wmissing-prototypes] 302 | __vdso_clock_gettime_stick(clockid_t clock, struct __kernel_old_timespec *ts) | ^ arch/sparc/vdso/vdso32/../vclock_gettime.c:301:9: note: declare 'static' if the function is not intended to be used outside of this translation unit 301 | notrace int | ^ | static arch/sparc/vdso/vdso32/../vclock_gettime.c:327:1: warning: no previous prototype for function '__vdso_gettimeofday' [-Wmissing-prototypes] 327 | __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz) | ^ arch/sparc/vdso/vdso32/../vclock_gettime.c:326:9: note: declare 'static' if the function is not intended to be used outside of this translation unit 326 | notrace int | ^ | static arch/sparc/vdso/vdso32/../vclock_gettime.c:363:1: warning: no previous prototype for function '__vdso_gettimeofday_stick' [-Wmissing-prototypes] 363 | __vdso_gettimeofday_stick(struct __kernel_old_timeval *tv, struct timezone *tz) | ^ arch/sparc/vdso/vdso32/../vclock_gettime.c:362:9: note: declare 'static' if the function is not intended to be used outside of this translation unit 362 | notrace int | ^ | static 29 warnings generated. vim +/readw_be +16 arch/sparc/include/asm/io.h 21dccddf45aae2 Jan Andersson 2011-05-10 9 21dccddf45aae2 Jan Andersson 2011-05-10 10 /* 21dccddf45aae2 Jan Andersson 2011-05-10 11 * Defines used for both SPARC32 and SPARC64 21dccddf45aae2 Jan Andersson 2011-05-10 12 */ 21dccddf45aae2 Jan Andersson 2011-05-10 13 21dccddf45aae2 Jan Andersson 2011-05-10 14 /* Big endian versions of memory read/write routines */ 21dccddf45aae2 Jan Andersson 2011-05-10 15 #define readb_be(__addr) __raw_readb(__addr) 21dccddf45aae2 Jan Andersson 2011-05-10 @16 #define readw_be(__addr) __raw_readw(__addr) 21dccddf45aae2 Jan Andersson 2011-05-10 @17 #define readl_be(__addr) __raw_readl(__addr) 21dccddf45aae2 Jan Andersson 2011-05-10 18 #define writeb_be(__b, __addr) __raw_writeb(__b, __addr) 21dccddf45aae2 Jan Andersson 2011-05-10 @19 #define writel_be(__w, __addr) __raw_writel(__w, __addr) 21dccddf45aae2 Jan Andersson 2011-05-10 @20 #define writew_be(__l, __addr) __raw_writew(__l, __addr) 21dccddf45aae2 Jan Andersson 2011-05-10 21 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
On Fri, Sep 26, 2025, at 12:53, Manikanta Guntupalli wrote: > Add MMIO accessors to support big-endian memory operations. These helpers > include {read, write}{w, l, q}_be() and {read, write}s{w, l, q}_be(), > which allows to access big-endian memory regions while returning > the results in the CPU’s native endianness. > > This provides a consistent interface to interact with hardware using > big-endian register layouts. > > Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com> > Reviewed-by: Frank Li <Frank.Li@nxp.com> My general feeling to this patch has not changed: I don't think these should be added in asm-generic/io.h at all, for multiple reasons - the {read,write}{w,l,q}be() helpers are redundant as they do the same as io{read,write}{16,32,64}be() for all practical purposes - Adding them caused build failures on some of the architectures that already have the same interfaces - You are not actually using any of them in your patch - The two functions that you do use, {read,write}sl() do not do what they claim to do and are impossible to use in portable code because they only work on byte-swapped FIFO registers when using a little-endian kernel. Please just fold whatever code you end up needing into your own driver as you had it in earlier versions. Arnd
© 2016 - 2025 Red Hat, Inc.