1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * I/O string operations |
4 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) |
5 | * Copyright (C) 2006 IBM Corporation |
6 | * |
7 | * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) |
8 | * and Paul Mackerras. |
9 | * |
10 | * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) |
11 | * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com) |
12 | * |
13 | * Rewritten in C by Stephen Rothwell. |
14 | */ |
15 | #include <linux/kernel.h> |
16 | #include <linux/types.h> |
17 | #include <linux/compiler.h> |
18 | #include <linux/export.h> |
19 | |
20 | #include <asm/io.h> |
21 | #include <asm/firmware.h> |
22 | #include <asm/bug.h> |
23 | |
24 | /* See definition in io.h */ |
25 | bool isa_io_special; |
26 | |
27 | void _insb(const volatile u8 __iomem *port, void *buf, long count) |
28 | { |
29 | u8 *tbuf = buf; |
30 | u8 tmp; |
31 | |
32 | if (unlikely(count <= 0)) |
33 | return; |
34 | asm volatile("sync" ); |
35 | do { |
36 | tmp = *(const volatile u8 __force *)port; |
37 | eieio(); |
38 | *tbuf++ = tmp; |
39 | } while (--count != 0); |
40 | asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); |
41 | } |
42 | EXPORT_SYMBOL(_insb); |
43 | |
44 | void _outsb(volatile u8 __iomem *port, const void *buf, long count) |
45 | { |
46 | const u8 *tbuf = buf; |
47 | |
48 | if (unlikely(count <= 0)) |
49 | return; |
50 | asm volatile("sync" ); |
51 | do { |
52 | *(volatile u8 __force *)port = *tbuf++; |
53 | } while (--count != 0); |
54 | asm volatile("sync" ); |
55 | } |
56 | EXPORT_SYMBOL(_outsb); |
57 | |
58 | void _insw_ns(const volatile u16 __iomem *port, void *buf, long count) |
59 | { |
60 | u16 *tbuf = buf; |
61 | u16 tmp; |
62 | |
63 | if (unlikely(count <= 0)) |
64 | return; |
65 | asm volatile("sync" ); |
66 | do { |
67 | tmp = *(const volatile u16 __force *)port; |
68 | eieio(); |
69 | *tbuf++ = tmp; |
70 | } while (--count != 0); |
71 | asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); |
72 | } |
73 | EXPORT_SYMBOL(_insw_ns); |
74 | |
75 | void _outsw_ns(volatile u16 __iomem *port, const void *buf, long count) |
76 | { |
77 | const u16 *tbuf = buf; |
78 | |
79 | if (unlikely(count <= 0)) |
80 | return; |
81 | asm volatile("sync" ); |
82 | do { |
83 | *(volatile u16 __force *)port = *tbuf++; |
84 | } while (--count != 0); |
85 | asm volatile("sync" ); |
86 | } |
87 | EXPORT_SYMBOL(_outsw_ns); |
88 | |
89 | void _insl_ns(const volatile u32 __iomem *port, void *buf, long count) |
90 | { |
91 | u32 *tbuf = buf; |
92 | u32 tmp; |
93 | |
94 | if (unlikely(count <= 0)) |
95 | return; |
96 | asm volatile("sync" ); |
97 | do { |
98 | tmp = *(const volatile u32 __force *)port; |
99 | eieio(); |
100 | *tbuf++ = tmp; |
101 | } while (--count != 0); |
102 | asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); |
103 | } |
104 | EXPORT_SYMBOL(_insl_ns); |
105 | |
106 | void _outsl_ns(volatile u32 __iomem *port, const void *buf, long count) |
107 | { |
108 | const u32 *tbuf = buf; |
109 | |
110 | if (unlikely(count <= 0)) |
111 | return; |
112 | asm volatile("sync" ); |
113 | do { |
114 | *(volatile u32 __force *)port = *tbuf++; |
115 | } while (--count != 0); |
116 | asm volatile("sync" ); |
117 | } |
118 | EXPORT_SYMBOL(_outsl_ns); |
119 | |
120 | #define IO_CHECK_ALIGN(v,a) ((((unsigned long)(v)) & ((a) - 1)) == 0) |
121 | |
122 | notrace void |
123 | _memset_io(volatile void __iomem *addr, int c, unsigned long n) |
124 | { |
125 | void *p = (void __force *)addr; |
126 | u32 lc = c; |
127 | lc |= lc << 8; |
128 | lc |= lc << 16; |
129 | |
130 | __asm__ __volatile__ ("sync" : : : "memory" ); |
131 | while(n && !IO_CHECK_ALIGN(p, 4)) { |
132 | *((volatile u8 *)p) = c; |
133 | p++; |
134 | n--; |
135 | } |
136 | while(n >= 4) { |
137 | *((volatile u32 *)p) = lc; |
138 | p += 4; |
139 | n -= 4; |
140 | } |
141 | while(n) { |
142 | *((volatile u8 *)p) = c; |
143 | p++; |
144 | n--; |
145 | } |
146 | __asm__ __volatile__ ("sync" : : : "memory" ); |
147 | } |
148 | EXPORT_SYMBOL(_memset_io); |
149 | |
150 | void _memcpy_fromio(void *dest, const volatile void __iomem *src, |
151 | unsigned long n) |
152 | { |
153 | void *vsrc = (void __force *) src; |
154 | |
155 | __asm__ __volatile__ ("sync" : : : "memory" ); |
156 | while(n && (!IO_CHECK_ALIGN(vsrc, 4) || !IO_CHECK_ALIGN(dest, 4))) { |
157 | *((u8 *)dest) = *((volatile u8 *)vsrc); |
158 | eieio(); |
159 | vsrc++; |
160 | dest++; |
161 | n--; |
162 | } |
163 | while(n >= 4) { |
164 | *((u32 *)dest) = *((volatile u32 *)vsrc); |
165 | eieio(); |
166 | vsrc += 4; |
167 | dest += 4; |
168 | n -= 4; |
169 | } |
170 | while(n) { |
171 | *((u8 *)dest) = *((volatile u8 *)vsrc); |
172 | eieio(); |
173 | vsrc++; |
174 | dest++; |
175 | n--; |
176 | } |
177 | __asm__ __volatile__ ("sync" : : : "memory" ); |
178 | } |
179 | EXPORT_SYMBOL(_memcpy_fromio); |
180 | |
181 | void _memcpy_toio(volatile void __iomem *dest, const void *src, unsigned long n) |
182 | { |
183 | void *vdest = (void __force *) dest; |
184 | |
185 | __asm__ __volatile__ ("sync" : : : "memory" ); |
186 | while(n && (!IO_CHECK_ALIGN(vdest, 4) || !IO_CHECK_ALIGN(src, 4))) { |
187 | *((volatile u8 *)vdest) = *((u8 *)src); |
188 | src++; |
189 | vdest++; |
190 | n--; |
191 | } |
192 | while(n >= 4) { |
193 | *((volatile u32 *)vdest) = *((volatile u32 *)src); |
194 | src += 4; |
195 | vdest += 4; |
196 | n-=4; |
197 | } |
198 | while(n) { |
199 | *((volatile u8 *)vdest) = *((u8 *)src); |
200 | src++; |
201 | vdest++; |
202 | n--; |
203 | } |
204 | __asm__ __volatile__ ("sync" : : : "memory" ); |
205 | } |
206 | EXPORT_SYMBOL(_memcpy_toio); |
207 | |