diff mbox series

[v3,14/34] xen/riscv: introduce io.h

Message ID 5d2c032481792a3fe5bd5f1cae42d95f6e9b54b1.1703255175.git.oleksii.kurochko@gmail.com (mailing list archive)
State Superseded
Headers show
Series Enable build of full Xen for RISC-V | expand

Commit Message

Oleksii Kurochko Dec. 22, 2023, 3:12 p.m. UTC
The header taken form Linux 6.4.0-rc1 and is based on
arch/riscv/include/asm/mmio.h.

Addionally, to the header was added definions of ioremap_*().

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
---
Changes in V3:
 - re-sync with linux kernel
 - update the commit message
---
Changes in V2:
 - Nothing changed. Only rebase.
---
 xen/arch/riscv/include/asm/io.h | 142 ++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
 create mode 100644 xen/arch/riscv/include/asm/io.h

Comments

Jan Beulich Jan. 15, 2024, 4:57 p.m. UTC | #1
On 22.12.2023 16:12, Oleksii Kurochko wrote:
> --- /dev/null
> +++ b/xen/arch/riscv/include/asm/io.h
> @@ -0,0 +1,142 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * {read,write}{b,w,l,q} based on arch/arm64/include/asm/io.h
> + *   which was based on arch/arm/include/io.h
> + *
> + * Copyright (C) 1996-2000 Russell King
> + * Copyright (C) 2012 ARM Ltd.
> + * Copyright (C) 2014 Regents of the University of California
> + */
> +
> +
> +#ifndef _ASM_RISCV_IO_H
> +#define _ASM_RISCV_IO_H
> +
> +#include <asm/byteorder.h>
> +
> +/*
> + * The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't
> + * change the properties of memory regions.  This should be fixed by the
> + * upcoming platform spec.
> + */
> +#define ioremap_nocache(addr, size) ioremap((addr), (size))
> +#define ioremap_wc(addr, size) ioremap((addr), (size))
> +#define ioremap_wt(addr, size) ioremap((addr), (size))

Nit: No need for the inner parentheses.

> +/* Generic IO read/write.  These perform native-endian accesses. */
> +#define __raw_writeb __raw_writeb
> +static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
> +{
> +	asm volatile("sb %0, 0(%1)" : : "r" (val), "r" (addr));
> +}
> +
> +#define __raw_writew __raw_writew
> +static inline void __raw_writew(u16 val, volatile void __iomem *addr)
> +{
> +	asm volatile("sh %0, 0(%1)" : : "r" (val), "r" (addr));
> +}
> +
> +#define __raw_writel __raw_writel
> +static inline void __raw_writel(u32 val, volatile void __iomem *addr)
> +{
> +	asm volatile("sw %0, 0(%1)" : : "r" (val), "r" (addr));
> +}
> +
> +#ifdef CONFIG_64BIT
> +#define __raw_writeq __raw_writeq
> +static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
> +{
> +	asm volatile("sd %0, 0(%1)" : : "r" (val), "r" (addr));
> +}
> +#endif
> +
> +#define __raw_readb __raw_readb
> +static inline u8 __raw_readb(const volatile void __iomem *addr)
> +{
> +	u8 val;
> +
> +	asm volatile("lb %0, 0(%1)" : "=r" (val) : "r" (addr));
> +	return val;
> +}
> +
> +#define __raw_readw __raw_readw
> +static inline u16 __raw_readw(const volatile void __iomem *addr)
> +{
> +	u16 val;
> +
> +	asm volatile("lh %0, 0(%1)" : "=r" (val) : "r" (addr));
> +	return val;
> +}
> +
> +#define __raw_readl __raw_readl
> +static inline u32 __raw_readl(const volatile void __iomem *addr)
> +{
> +	u32 val;
> +
> +	asm volatile("lw %0, 0(%1)" : "=r" (val) : "r" (addr));
> +	return val;
> +}
> +
> +#ifdef CONFIG_64BIT
> +#define __raw_readq __raw_readq
> +static inline u64 __raw_readq(const volatile void __iomem *addr)
> +{
> +	u64 val;
> +
> +	asm volatile("ld %0, 0(%1)" : "=r" (val) : "r" (addr));
> +	return val;
> +}
> +#endif
> +
> +/*
> + * Unordered I/O memory access primitives.  These are even more relaxed than
> + * the relaxed versions, as they don't even order accesses between successive
> + * operations to the I/O regions.
> + */
> +#define readb_cpu(c)		({ u8  __r = __raw_readb(c); __r; })
> +#define readw_cpu(c)		({ u16 __r = le16_to_cpu((__force __le16)__raw_readw(c)); __r; })
> +#define readl_cpu(c)		({ u32 __r = le32_to_cpu((__force __le32)__raw_readl(c)); __r; })
> +
> +#define writeb_cpu(v,c)		((void)__raw_writeb((v),(c)))
> +#define writew_cpu(v,c)		((void)__raw_writew((__force u16)cpu_to_le16(v),(c)))
> +#define writel_cpu(v,c)		((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
> +
> +#ifdef CONFIG_64BIT
> +#define readq_cpu(c)		({ u64 __r = le64_to_cpu((__force __le64)__raw_readq(c)); __r; })
> +#define writeq_cpu(v,c)		((void)__raw_writeq((__force u64)cpu_to_le64(v),(c)))
> +#endif

How come there are endianness assumptions here on the MMIO accessed?

As a file-wide remark: While I don't mind you using u<N> here for now,
presumably to stay close to Linux, eventually - as we make progress with
the conversion to uint<N>_t - this will need to diverge anyway.

> +/*
> + * I/O memory access primitives. Reads are ordered relative to any
> + * following Normal memory access. Writes are ordered relative to any prior
> + * Normal memory access.  The memory barriers here are necessary as RISC-V
> + * doesn't define any ordering between the memory space and the I/O space.
> + */
> +#define __io_br()	do {} while (0)

Nit: Why are this and ...

> +#define __io_ar(v)	__asm__ __volatile__ ("fence i,r" : : : "memory");
> +#define __io_bw()	__asm__ __volatile__ ("fence w,o" : : : "memory");
> +#define __io_aw()	do { } while (0)

... this not expanding exactly the same?

Jan
Oleksii Kurochko Jan. 16, 2024, 3:20 p.m. UTC | #2
On Mon, 2024-01-15 at 17:57 +0100, Jan Beulich wrote:
> On 22.12.2023 16:12, Oleksii Kurochko wrote:
> > --- /dev/null
> > +++ b/xen/arch/riscv/include/asm/io.h
> > @@ -0,0 +1,142 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * {read,write}{b,w,l,q} based on arch/arm64/include/asm/io.h
> > + *   which was based on arch/arm/include/io.h
> > + *
> > + * Copyright (C) 1996-2000 Russell King
> > + * Copyright (C) 2012 ARM Ltd.
> > + * Copyright (C) 2014 Regents of the University of California
> > + */
> > +
> > +
> > +#ifndef _ASM_RISCV_IO_H
> > +#define _ASM_RISCV_IO_H
> > +
> > +#include <asm/byteorder.h>
> > +
> > +/*
> > + * The RISC-V ISA doesn't yet specify how to query or modify PMAs,
> > so we can't
> > + * change the properties of memory regions.  This should be fixed
> > by the
> > + * upcoming platform spec.
> > + */
> > +#define ioremap_nocache(addr, size) ioremap((addr), (size))
> > +#define ioremap_wc(addr, size) ioremap((addr), (size))
> > +#define ioremap_wt(addr, size) ioremap((addr), (size))
> 
> Nit: No need for the inner parentheses.
Thanks. I'll update that place.

> 
> > +/* Generic IO read/write.  These perform native-endian accesses.
> > */
> > +#define __raw_writeb __raw_writeb
> > +static inline void __raw_writeb(u8 val, volatile void __iomem
> > *addr)
> > +{
> > +	asm volatile("sb %0, 0(%1)" : : "r" (val), "r" (addr));
> > +}
> > +
> > +#define __raw_writew __raw_writew
> > +static inline void __raw_writew(u16 val, volatile void __iomem
> > *addr)
> > +{
> > +	asm volatile("sh %0, 0(%1)" : : "r" (val), "r" (addr));
> > +}
> > +
> > +#define __raw_writel __raw_writel
> > +static inline void __raw_writel(u32 val, volatile void __iomem
> > *addr)
> > +{
> > +	asm volatile("sw %0, 0(%1)" : : "r" (val), "r" (addr));
> > +}
> > +
> > +#ifdef CONFIG_64BIT
> > +#define __raw_writeq __raw_writeq
> > +static inline void __raw_writeq(u64 val, volatile void __iomem
> > *addr)
> > +{
> > +	asm volatile("sd %0, 0(%1)" : : "r" (val), "r" (addr));
> > +}
> > +#endif
> > +
> > +#define __raw_readb __raw_readb
> > +static inline u8 __raw_readb(const volatile void __iomem *addr)
> > +{
> > +	u8 val;
> > +
> > +	asm volatile("lb %0, 0(%1)" : "=r" (val) : "r" (addr));
> > +	return val;
> > +}
> > +
> > +#define __raw_readw __raw_readw
> > +static inline u16 __raw_readw(const volatile void __iomem *addr)
> > +{
> > +	u16 val;
> > +
> > +	asm volatile("lh %0, 0(%1)" : "=r" (val) : "r" (addr));
> > +	return val;
> > +}
> > +
> > +#define __raw_readl __raw_readl
> > +static inline u32 __raw_readl(const volatile void __iomem *addr)
> > +{
> > +	u32 val;
> > +
> > +	asm volatile("lw %0, 0(%1)" : "=r" (val) : "r" (addr));
> > +	return val;
> > +}
> > +
> > +#ifdef CONFIG_64BIT
> > +#define __raw_readq __raw_readq
> > +static inline u64 __raw_readq(const volatile void __iomem *addr)
> > +{
> > +	u64 val;
> > +
> > +	asm volatile("ld %0, 0(%1)" : "=r" (val) : "r" (addr));
> > +	return val;
> > +}
> > +#endif
> > +
> > +/*
> > + * Unordered I/O memory access primitives.  These are even more
> > relaxed than
> > + * the relaxed versions, as they don't even order accesses between
> > successive
> > + * operations to the I/O regions.
> > + */
> > +#define readb_cpu(c)		({ u8  __r = __raw_readb(c); __r;
> > })
> > +#define readw_cpu(c)		({ u16 __r = le16_to_cpu((__force
> > __le16)__raw_readw(c)); __r; })
> > +#define readl_cpu(c)		({ u32 __r = le32_to_cpu((__force
> > __le32)__raw_readl(c)); __r; })
> > +
> > +#define
> > writeb_cpu(v,c)		((void)__raw_writeb((v),(c)))
> > +#define
> > writew_cpu(v,c)		((void)__raw_writew((__force u16)cpu_to_le16(v),(c)))
> > +#define
> > writel_cpu(v,c)		((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
> > +
> > +#ifdef CONFIG_64BIT
> > +#define readq_cpu(c)		({ u64 __r = le64_to_cpu((__force
> > __le64)__raw_readq(c)); __r; })
> > +#define
> > writeq_cpu(v,c)		((void)__raw_writeq((__force u64)cpu_to_le64(v),(c)))
> > +#endif
> 
> How come there are endianness assumptions here on the MMIO accessed?
It is a hard story.

As you might expect it was copy from Linux Kernel where it was decided
to follow only LE way:
https://patchwork.kernel.org/project/linux-riscv/patch/20190411115623.5749-3-hch@lst.de/
One of the answers of the author of the commit:
    And we don't know if Linux will be around if that ever changes.
    The point is:
     a) the current RISC-V spec is LE only
     b) the current linux port is LE only except for this little bit
    There is no point in leaving just this bitrotting code around.  It
    just confuses developers, (very very slightly) slows down compiles
    and will bitrot.  It also won't be any significant help to a future
    developer down the road doing a hypothetical BE RISC-V Linux port.

From the specs [1, p.5 ] it is mentioned that:
   The base ISA has been defined to have a little-endian memory system, 
   with big-endian or bi-endian as non-standard variants.

And in [3, p.6]:
   RISC-V base ISAs have either little-endian or big-endian memory 
   systems, with the privileged architecture further defining bi-endian 
   operation. Instructions are stored in memory as a sequence of 16-bit 
   little-endian parcels, regardless of memory system endianness. Parcels
   forming one instruction are stored at increasing halfword addresses, 
   with the lowest-addressed parcel holding the lowest-numbered bits in 
   the instruction specification.
   
    We originally chose little-endian byte ordering for the RISC-V memory
   system because little-endian systems are currently dominant 
   commercially (all x86 systems; iOS, Android, and Windows for ARM). A 
   minor point is that we have also found little-endian memory systems to
   be more natural for hardware designers. However, certain application 
   areas, such as IP networking, operate on big-endian data structures, 
   and certain legacy code bases have been built assuming big-endian 
   processors, so we have defined big-endian and bi-endian variants of 
   RISC-V.
   
    We have to fix the order in which instruction parcels are stored in 
   memory, independent of memory system endianness, to ensure that the 
   length-encoding bits always appear first in halfword address order. 
   This allows the length of a variable-length instruction to be quickly 
   determined by an instruction-fetch unit by examining only the first few
   bits of the first 16-bit instruction parcel.

[ this part is from source [2] which I can't find in [3] for some
uknown reason ]
   We further make the instruction parcels themselves little-endian to
   decouple the instruction encoding from the memory system endianness
   altogether. This design benefits both software tooling and bi-endian
   hardware. Otherwise, for instance, a RISC-V assembler or disassembler
   would always need to know the intended active endianness, despite that
   in bi-endian systems, the endianness mode might change dynamically
   during execution. In contrast, by giving instructions a fixed
   endianness, it is sometimes possible for carefully written software to
   be endianness-agnostic even in binary form, much like position-
   independent code.
   
   The choice to have instructions be only little-endian does have
   consequences, however, for RISC-V software that encodes or decodes
   machine instructions. Big-endian JIT compilers, for example, must swap
   the byte order when storing to instruction memory.
   
   Once we had decided to fix on a little-endian instruction encoding,
   this naturally led to placing the length-encoding bits in the LSB
   positions of the instruction format to avoid breaking up opcode fields.


[1] https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf
[2] https://five-embeddev.com/riscv-isa-manual/latest/intro.html
[3] https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf
> 
> As a file-wide remark: While I don't mind you using u<N> here for
> now,
> presumably to stay close to Linux, eventually - as we make progress
> with
> the conversion to uint<N>_t - this will need to diverge anyway.
Then I'll use uint<N>_t everywhere from the start. I am pretty sure
this file doesn't require sync often, so we can use  Xen style instead
of Linux.

> 
> > +/*
> > + * I/O memory access primitives. Reads are ordered relative to any
> > + * following Normal memory access. Writes are ordered relative to
> > any prior
> > + * Normal memory access.  The memory barriers here are necessary
> > as RISC-V
> > + * doesn't define any ordering between the memory space and the
> > I/O space.
> > + */
> > +#define __io_br()	do {} while (0)
> 
> Nit: Why are this and ...
> 
> > +#define __io_ar(v)	__asm__ __volatile__ ("fence i,r" : : :
> > "memory");
> > +#define __io_bw()	__asm__ __volatile__ ("fence w,o" : : :
> > "memory");
> > +#define __io_aw()	do { } while (0)
> 
> ... this not expanding exactly the same?
I don't know the specific reason, it was done so in Linux kernel in
case when CONFIG_MMIOWB isn't supported:
https://elixir.bootlin.com/linux/latest/source/include/asm-generic/mmiowb.h#L61

https://elixir.bootlin.com/linux/latest/source/arch/riscv/include/asm/mmio.h#L136



~ Oleksii
Jan Beulich Jan. 16, 2024, 4:09 p.m. UTC | #3
On 16.01.2024 16:20, Oleksii wrote:
> On Mon, 2024-01-15 at 17:57 +0100, Jan Beulich wrote:
>> On 22.12.2023 16:12, Oleksii Kurochko wrote:
>>> +/*
>>> + * Unordered I/O memory access primitives.  These are even more
>>> relaxed than
>>> + * the relaxed versions, as they don't even order accesses between
>>> successive
>>> + * operations to the I/O regions.
>>> + */
>>> +#define readb_cpu(c)		({ u8  __r = __raw_readb(c); __r;
>>> })
>>> +#define readw_cpu(c)		({ u16 __r = le16_to_cpu((__force
>>> __le16)__raw_readw(c)); __r; })
>>> +#define readl_cpu(c)		({ u32 __r = le32_to_cpu((__force
>>> __le32)__raw_readl(c)); __r; })
>>> +
>>> +#define
>>> writeb_cpu(v,c)		((void)__raw_writeb((v),(c)))
>>> +#define
>>> writew_cpu(v,c)		((void)__raw_writew((__force u16)cpu_to_le16(v),(c)))
>>> +#define
>>> writel_cpu(v,c)		((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
>>> +
>>> +#ifdef CONFIG_64BIT
>>> +#define readq_cpu(c)		({ u64 __r = le64_to_cpu((__force
>>> __le64)__raw_readq(c)); __r; })
>>> +#define
>>> writeq_cpu(v,c)		((void)__raw_writeq((__force u64)cpu_to_le64(v),(c)))
>>> +#endif
>>
>> How come there are endianness assumptions here on the MMIO accessed?
> It is a hard story.
> 
> As you might expect it was copy from Linux Kernel where it was decided
> to follow only LE way:
> https://patchwork.kernel.org/project/linux-riscv/patch/20190411115623.5749-3-hch@lst.de/
> One of the answers of the author of the commit:
>     And we don't know if Linux will be around if that ever changes.
>     The point is:
>      a) the current RISC-V spec is LE only
>      b) the current linux port is LE only except for this little bit
>     There is no point in leaving just this bitrotting code around.  It
>     just confuses developers, (very very slightly) slows down compiles
>     and will bitrot.  It also won't be any significant help to a future
>     developer down the road doing a hypothetical BE RISC-V Linux port.

Reads to me like a justification to _omit_ the cpu_to_le<N>().

Jan
Oleksii Kurochko Jan. 31, 2024, 5:30 p.m. UTC | #4
On Tue, 2024-01-16 at 17:09 +0100, Jan Beulich wrote:
> On 16.01.2024 16:20, Oleksii wrote:
> > On Mon, 2024-01-15 at 17:57 +0100, Jan Beulich wrote:
> > > On 22.12.2023 16:12, Oleksii Kurochko wrote:
> > > > +/*
> > > > + * Unordered I/O memory access primitives.  These are even
> > > > more
> > > > relaxed than
> > > > + * the relaxed versions, as they don't even order accesses
> > > > between
> > > > successive
> > > > + * operations to the I/O regions.
> > > > + */
> > > > +#define readb_cpu(c)		({ u8  __r = __raw_readb(c);
> > > > __r;
> > > > })
> > > > +#define readw_cpu(c)		({ u16 __r =
> > > > le16_to_cpu((__force
> > > > __le16)__raw_readw(c)); __r; })
> > > > +#define readl_cpu(c)		({ u32 __r =
> > > > le32_to_cpu((__force
> > > > __le32)__raw_readl(c)); __r; })
> > > > +
> > > > +#define
> > > > writeb_cpu(v,c)		((void)__raw_writeb((v),(c)))
> > > > +#define
> > > > writew_cpu(v,c)		((void)__raw_writew((__force
> > > > u16)cpu_to_le16(v),(c)))
> > > > +#define
> > > > writel_cpu(v,c)		((void)__raw_writel((__force
> > > > u32)cpu_to_le32(v),(c)))
> > > > +
> > > > +#ifdef CONFIG_64BIT
> > > > +#define readq_cpu(c)		({ u64 __r =
> > > > le64_to_cpu((__force
> > > > __le64)__raw_readq(c)); __r; })
> > > > +#define
> > > > writeq_cpu(v,c)		((void)__raw_writeq((__force
> > > > u64)cpu_to_le64(v),(c)))
> > > > +#endif
> > > 
> > > How come there are endianness assumptions here on the MMIO
> > > accessed?
> > It is a hard story.
> > 
> > As you might expect it was copy from Linux Kernel where it was
> > decided
> > to follow only LE way:
> > https://patchwork.kernel.org/project/linux-riscv/patch/20190411115623.5749-3-hch@lst.de/
> > One of the answers of the author of the commit:
> >     And we don't know if Linux will be around if that ever changes.
> >     The point is:
> >      a) the current RISC-V spec is LE only
> >      b) the current linux port is LE only except for this little
> > bit
> >     There is no point in leaving just this bitrotting code around. 
> > It
> >     just confuses developers, (very very slightly) slows down
> > compiles
> >     and will bitrot.  It also won't be any significant help to a
> > future
> >     developer down the road doing a hypothetical BE RISC-V Linux
> > port.
> 
> Reads to me like a justification to _omit_ the cpu_to_le<N>().
Looks like we can omit cpu_to_le<N>(). Even docs say that memory system
is little-endian except:
   However, certain application areas, such as IP networking, operate
   on big-endian data structures, and certain legacy code bases have
   been built assuming big-endian
   processors, so we expect that future specifications will describe
   big-endian or bi-endian variants
   of RISC-V.
   
~ Oleksii
diff mbox series

Patch

diff --git a/xen/arch/riscv/include/asm/io.h b/xen/arch/riscv/include/asm/io.h
new file mode 100644
index 0000000000..ead466eb2d
--- /dev/null
+++ b/xen/arch/riscv/include/asm/io.h
@@ -0,0 +1,142 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * {read,write}{b,w,l,q} based on arch/arm64/include/asm/io.h
+ *   which was based on arch/arm/include/io.h
+ *
+ * Copyright (C) 1996-2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2014 Regents of the University of California
+ */
+
+
+#ifndef _ASM_RISCV_IO_H
+#define _ASM_RISCV_IO_H
+
+#include <asm/byteorder.h>
+
+/*
+ * The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't
+ * change the properties of memory regions.  This should be fixed by the
+ * upcoming platform spec.
+ */
+#define ioremap_nocache(addr, size) ioremap((addr), (size))
+#define ioremap_wc(addr, size) ioremap((addr), (size))
+#define ioremap_wt(addr, size) ioremap((addr), (size))
+
+/* Generic IO read/write.  These perform native-endian accesses. */
+#define __raw_writeb __raw_writeb
+static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
+{
+	asm volatile("sb %0, 0(%1)" : : "r" (val), "r" (addr));
+}
+
+#define __raw_writew __raw_writew
+static inline void __raw_writew(u16 val, volatile void __iomem *addr)
+{
+	asm volatile("sh %0, 0(%1)" : : "r" (val), "r" (addr));
+}
+
+#define __raw_writel __raw_writel
+static inline void __raw_writel(u32 val, volatile void __iomem *addr)
+{
+	asm volatile("sw %0, 0(%1)" : : "r" (val), "r" (addr));
+}
+
+#ifdef CONFIG_64BIT
+#define __raw_writeq __raw_writeq
+static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
+{
+	asm volatile("sd %0, 0(%1)" : : "r" (val), "r" (addr));
+}
+#endif
+
+#define __raw_readb __raw_readb
+static inline u8 __raw_readb(const volatile void __iomem *addr)
+{
+	u8 val;
+
+	asm volatile("lb %0, 0(%1)" : "=r" (val) : "r" (addr));
+	return val;
+}
+
+#define __raw_readw __raw_readw
+static inline u16 __raw_readw(const volatile void __iomem *addr)
+{
+	u16 val;
+
+	asm volatile("lh %0, 0(%1)" : "=r" (val) : "r" (addr));
+	return val;
+}
+
+#define __raw_readl __raw_readl
+static inline u32 __raw_readl(const volatile void __iomem *addr)
+{
+	u32 val;
+
+	asm volatile("lw %0, 0(%1)" : "=r" (val) : "r" (addr));
+	return val;
+}
+
+#ifdef CONFIG_64BIT
+#define __raw_readq __raw_readq
+static inline u64 __raw_readq(const volatile void __iomem *addr)
+{
+	u64 val;
+
+	asm volatile("ld %0, 0(%1)" : "=r" (val) : "r" (addr));
+	return val;
+}
+#endif
+
+/*
+ * Unordered I/O memory access primitives.  These are even more relaxed than
+ * the relaxed versions, as they don't even order accesses between successive
+ * operations to the I/O regions.
+ */
+#define readb_cpu(c)		({ u8  __r = __raw_readb(c); __r; })
+#define readw_cpu(c)		({ u16 __r = le16_to_cpu((__force __le16)__raw_readw(c)); __r; })
+#define readl_cpu(c)		({ u32 __r = le32_to_cpu((__force __le32)__raw_readl(c)); __r; })
+
+#define writeb_cpu(v,c)		((void)__raw_writeb((v),(c)))
+#define writew_cpu(v,c)		((void)__raw_writew((__force u16)cpu_to_le16(v),(c)))
+#define writel_cpu(v,c)		((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
+
+#ifdef CONFIG_64BIT
+#define readq_cpu(c)		({ u64 __r = le64_to_cpu((__force __le64)__raw_readq(c)); __r; })
+#define writeq_cpu(v,c)		((void)__raw_writeq((__force u64)cpu_to_le64(v),(c)))
+#endif
+
+/*
+ * I/O memory access primitives. Reads are ordered relative to any
+ * following Normal memory access. Writes are ordered relative to any prior
+ * Normal memory access.  The memory barriers here are necessary as RISC-V
+ * doesn't define any ordering between the memory space and the I/O space.
+ */
+#define __io_br()	do {} while (0)
+#define __io_ar(v)	__asm__ __volatile__ ("fence i,r" : : : "memory");
+#define __io_bw()	__asm__ __volatile__ ("fence w,o" : : : "memory");
+#define __io_aw()	do { } while (0)
+
+#define readb(c)	({ u8  __v; __io_br(); __v = readb_cpu(c); __io_ar(__v); __v; })
+#define readw(c)	({ u16 __v; __io_br(); __v = readw_cpu(c); __io_ar(__v); __v; })
+#define readl(c)	({ u32 __v; __io_br(); __v = readl_cpu(c); __io_ar(__v); __v; })
+
+#define writeb(v,c)	({ __io_bw(); writeb_cpu((v),(c)); __io_aw(); })
+#define writew(v,c)	({ __io_bw(); writew_cpu((v),(c)); __io_aw(); })
+#define writel(v,c)	({ __io_bw(); writel_cpu((v),(c)); __io_aw(); })
+
+#ifdef CONFIG_64BIT
+#define readq(c)	({ u64 __v; __io_br(); __v = readq_cpu(c); __io_ar(__v); __v; })
+#define writeq(v,c)	({ __io_bw(); writeq_cpu((v),(c)); __io_aw(); })
+#endif
+
+#endif /* _ASM_RISCV_IO_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */