Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARC: io.h: Implement reads{x}()/writes{x}()

Some ARC CPU's do not support unaligned loads/stores. Currently, generic
implementation of reads{b/w/l}()/writes{b/w/l}() is being used with ARC.
This can lead to misfunction of some drivers as generic functions do a
plain dereference of a pointer that can be unaligned.

Let's use {get/put}_unaligned() helpers instead of plain dereference of
pointer in order to fix. The helpers allow to get and store data from an
unaligned address whilst preserving the CPU internal alignment.
According to [1], the use of these helpers are costly in terms of
performance so we added an initial check for a buffer already aligned so
that the usage of the helpers can be avoided, when possible.

[1] Documentation/unaligned-memory-access.txt

Cc: Alexey Brodkin <abrodkin@synopsys.com>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: David Laight <David.Laight@ACULAB.COM>
Tested-by: Vitor Soares <soares@synopsys.com>
Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>

authored by

Jose Abreu and committed by
Vineet Gupta
10d44343 b7cc40c3

+72
+72
arch/arc/include/asm/io.h
··· 12 12 #include <linux/types.h> 13 13 #include <asm/byteorder.h> 14 14 #include <asm/page.h> 15 + #include <asm/unaligned.h> 15 16 16 17 #ifdef CONFIG_ISA_ARCV2 17 18 #include <asm/barrier.h> ··· 95 94 return w; 96 95 } 97 96 97 + /* 98 + * {read,write}s{b,w,l}() repeatedly access the same IO address in 99 + * native endianness in 8-, 16-, 32-bit chunks {into,from} memory, 100 + * @count times 101 + */ 102 + #define __raw_readsx(t,f) \ 103 + static inline void __raw_reads##f(const volatile void __iomem *addr, \ 104 + void *ptr, unsigned int count) \ 105 + { \ 106 + bool is_aligned = ((unsigned long)ptr % ((t) / 8)) == 0; \ 107 + u##t *buf = ptr; \ 108 + \ 109 + if (!count) \ 110 + return; \ 111 + \ 112 + /* Some ARC CPU's don't support unaligned accesses */ \ 113 + if (is_aligned) { \ 114 + do { \ 115 + u##t x = __raw_read##f(addr); \ 116 + *buf++ = x; \ 117 + } while (--count); \ 118 + } else { \ 119 + do { \ 120 + u##t x = __raw_read##f(addr); \ 121 + put_unaligned(x, buf++); \ 122 + } while (--count); \ 123 + } \ 124 + } 125 + 126 + #define __raw_readsb __raw_readsb 127 + __raw_readsx(8, b) 128 + #define __raw_readsw __raw_readsw 129 + __raw_readsx(16, w) 130 + #define __raw_readsl __raw_readsl 131 + __raw_readsx(32, l) 132 + 98 133 #define __raw_writeb __raw_writeb 99 134 static inline void __raw_writeb(u8 b, volatile void __iomem *addr) 100 135 { ··· 163 126 164 127 } 165 128 129 + #define __raw_writesx(t,f) \ 130 + static inline void __raw_writes##f(volatile void __iomem *addr, \ 131 + const void *ptr, unsigned int count) \ 132 + { \ 133 + bool is_aligned = ((unsigned long)ptr % ((t) / 8)) == 0; \ 134 + const u##t *buf = ptr; \ 135 + \ 136 + if (!count) \ 137 + return; \ 138 + \ 139 + /* Some ARC CPU's don't support unaligned accesses */ \ 140 + if (is_aligned) { \ 141 + do { \ 142 + __raw_write##f(*buf++, addr); \ 143 + } while (--count); \ 144 + } else { \ 145 + do { \ 146 + __raw_write##f(get_unaligned(buf++), addr); \ 147 + } while (--count); \ 148 + } \ 149 + } 150 + 151 + #define __raw_writesb __raw_writesb 152 + __raw_writesx(8, b) 153 + #define __raw_writesw __raw_writesw 154 + __raw_writesx(16, w) 155 + #define __raw_writesl __raw_writesl 156 + __raw_writesx(32, l) 157 + 166 158 /* 167 159 * MMIO can also get buffered/optimized in micro-arch, so barriers needed 168 160 * Based on ARM model for the typical use case ··· 207 141 #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; }) 208 142 #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; }) 209 143 #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) 144 + #define readsb(p,d,l) ({ __raw_readsb(p,d,l); __iormb(); }) 145 + #define readsw(p,d,l) ({ __raw_readsw(p,d,l); __iormb(); }) 146 + #define readsl(p,d,l) ({ __raw_readsl(p,d,l); __iormb(); }) 210 147 211 148 #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); }) 212 149 #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); }) 213 150 #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); }) 151 + #define writesb(p,d,l) ({ __iowmb(); __raw_writesb(p,d,l); }) 152 + #define writesw(p,d,l) ({ __iowmb(); __raw_writesw(p,d,l); }) 153 + #define writesl(p,d,l) ({ __iowmb(); __raw_writesl(p,d,l); }) 214 154 215 155 /* 216 156 * Relaxed API for drivers which can handle barrier ordering themselves