]>
Commit | Line | Data |
---|---|---|
04277e02 | 1 | /* Copyright (C) 1992-2019 Free Software Foundation, Inc. |
dc37f3e5 UD |
2 | This file is part of the GNU C Library. |
3 | Contributed by David Mosberger. | |
56759be2 | 4 | |
dc37f3e5 | 5 | The GNU C Library is free software; you can redistribute it and/or |
3214b89b AJ |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
56759be2 | 9 | |
dc37f3e5 UD |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
3214b89b | 13 | Lesser General Public License for more details. |
56759be2 | 14 | |
3214b89b | 15 | You should have received a copy of the GNU Lesser General Public |
ab84e3ff PE |
16 | License along with the GNU C Library. If not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
56759be2 RM |
18 | |
19 | /* I/O access is restricted to ISA port space (ports 0..65535). | |
dc37f3e5 UD |
20 | Modern devices hopefully are sane enough not to put any performance |
21 | critical registers in i/o space. | |
56759be2 | 22 | |
194b9b3b RH |
23 | On the first call to ioperm, the entire (E)ISA port space is mapped |
24 | into the virtual address space at address io.base. mprotect calls | |
25 | are then used to enable/disable access to ports. Per page, there | |
26 | are PAGE_SIZE>>IO_SHIFT I/O ports (e.g., 256 ports on a Low Cost Alpha | |
27 | based system using 8KB pages). | |
56759be2 | 28 | |
dc37f3e5 UD |
29 | Keep in mind that this code should be able to run in a 32bit address |
30 | space. It is therefore unreasonable to expect mmap'ing the entire | |
31 | sparse address space would work (e.g., the Low Cost Alpha chip has an | |
32 | I/O address space that's 512MB large!). */ | |
56759be2 | 33 | |
91d46f8a RH |
34 | /* Make sure the ldbu/stb asms below are not expaneded to macros. */ |
35 | #ifndef __alpha_bwx__ | |
36 | asm(".arch ev56"); | |
37 | #endif | |
38 | ||
56759be2 RM |
39 | #include <errno.h> |
40 | #include <fcntl.h> | |
41 | #include <stdio.h> | |
d44e5674 | 42 | #include <ctype.h> |
15999cfb | 43 | #include <stdlib.h> |
56759be2 | 44 | #include <string.h> |
15999cfb | 45 | #include <unistd.h> |
56759be2 RM |
46 | |
47 | #include <sys/types.h> | |
48 | #include <sys/mman.h> | |
194b9b3b | 49 | #include <sys/io.h> |
56759be2 | 50 | |
194b9b3b RH |
51 | #include <sysdep.h> |
52 | #include <sys/syscall.h> | |
56759be2 | 53 | |
15999cfb RM |
54 | #define PATH_ALPHA_SYSTYPE "/etc/alpha_systype" |
55 | #define PATH_CPUINFO "/proc/cpuinfo" | |
56759be2 RM |
56 | |
57 | #define MAX_PORT 0x10000 | |
194b9b3b | 58 | #define vip volatile int * |
56759be2 | 59 | #define vuip volatile unsigned int * |
194b9b3b RH |
60 | #define vusp volatile unsigned short * |
61 | #define vucp volatile unsigned char * | |
56759be2 | 62 | |
194b9b3b RH |
63 | #define JENSEN_IO_BASE (0x300000000UL) |
64 | #define JENSEN_SPARSE_MEM (0x200000000UL) | |
15999cfb | 65 | |
2521516d UD |
66 | /* With respect to the I/O architecture, APECS and LCA are identical, |
67 | so the following defines apply to LCA as well. */ | |
194b9b3b RH |
68 | #define APECS_IO_BASE (0x1c0000000UL) |
69 | #define APECS_SPARSE_MEM (0x200000000UL) | |
70 | #define APECS_DENSE_MEM (0x300000000UL) | |
15999cfb | 71 | |
194b9b3b RH |
72 | /* The same holds for CIA and PYXIS, except for PYXIS we prefer BWX. */ |
73 | #define CIA_IO_BASE (0x8580000000UL) | |
74 | #define CIA_SPARSE_MEM (0x8000000000UL) | |
75 | #define CIA_DENSE_MEM (0x8600000000UL) | |
15999cfb | 76 | |
194b9b3b RH |
77 | #define PYXIS_IO_BASE (0x8900000000UL) |
78 | #define PYXIS_DENSE_MEM (0x8800000000UL) | |
56759be2 | 79 | |
194b9b3b RH |
80 | /* SABLE is EV4, GAMMA is EV5 */ |
81 | #define T2_IO_BASE (0x3a0000000UL) | |
82 | #define T2_SPARSE_MEM (0x200000000UL) | |
83 | #define T2_DENSE_MEM (0x3c0000000UL) | |
a8d236a8 | 84 | |
194b9b3b RH |
85 | #define GAMMA_IO_BASE (0x83a0000000UL) |
86 | #define GAMMA_SPARSE_MEM (0x8200000000UL) | |
87 | #define GAMMA_DENSE_MEM (0x83c0000000UL) | |
a8d236a8 | 88 | |
a8d236a8 | 89 | /* NOTE: these are hardwired to PCI bus 0 addresses!!! */ |
194b9b3b RH |
90 | #define MCPCIA_IO_BASE (0xf980000000UL) |
91 | #define MCPCIA_SPARSE_MEM (0xf800000000UL) | |
92 | #define MCPCIA_DENSE_MEM (0xf900000000UL) | |
93 | ||
94 | /* Tsunami and Irongate use the same offsets, at least for hose 0. */ | |
95 | #define TSUNAMI_IO_BASE (0x801fc000000UL) | |
96 | #define TSUNAMI_DENSE_MEM (0x80000000000UL) | |
a8d236a8 | 97 | |
194b9b3b RH |
98 | /* Polaris has SPARSE space, but we prefer to use only DENSE |
99 | because of some idiosyncracies in actually using SPARSE. */ | |
100 | #define POLARIS_IO_BASE (0xf9fc000000UL) | |
101 | #define POLARIS_DENSE_MEM (0xf900000000UL) | |
05246799 | 102 | |
dc37f3e5 | 103 | typedef enum { |
194b9b3b | 104 | IOSYS_UNKNOWN, IOSYS_JENSEN, IOSYS_APECS, IOSYS_CIA, IOSYS_PYXIS, IOSYS_T2, |
05246799 UD |
105 | IOSYS_TSUNAMI, IOSYS_MCPCIA, IOSYS_GAMMA, IOSYS_POLARIS, |
106 | IOSYS_CPUDEP, IOSYS_PCIDEP | |
56759be2 RM |
107 | } iosys_t; |
108 | ||
05246799 UD |
109 | typedef enum { |
110 | IOSWIZZLE_JENSEN, IOSWIZZLE_SPARSE, IOSWIZZLE_DENSE | |
111 | } ioswizzle_t; | |
112 | ||
a8d236a8 | 113 | static struct io_system { |
a8d236a8 UD |
114 | unsigned long int bus_memory_base; |
115 | unsigned long int sparse_bus_mem_base; | |
116 | unsigned long int bus_io_base; | |
117 | } io_system[] = { /* NOTE! must match iosys_t enumeration */ | |
194b9b3b RH |
118 | /* UNKNOWN */ {0, 0, 0}, |
119 | /* JENSEN */ {0, JENSEN_SPARSE_MEM, JENSEN_IO_BASE}, | |
120 | /* APECS */ {APECS_DENSE_MEM, APECS_SPARSE_MEM, APECS_IO_BASE}, | |
121 | /* CIA */ {CIA_DENSE_MEM, CIA_SPARSE_MEM, CIA_IO_BASE}, | |
122 | /* PYXIS */ {PYXIS_DENSE_MEM, 0, PYXIS_IO_BASE}, | |
123 | /* T2 */ {T2_DENSE_MEM, T2_SPARSE_MEM, T2_IO_BASE}, | |
124 | /* TSUNAMI */ {TSUNAMI_DENSE_MEM, 0, TSUNAMI_IO_BASE}, | |
125 | /* MCPCIA */ {MCPCIA_DENSE_MEM, MCPCIA_SPARSE_MEM, MCPCIA_IO_BASE}, | |
126 | /* GAMMA */ {GAMMA_DENSE_MEM, GAMMA_SPARSE_MEM, GAMMA_IO_BASE}, | |
127 | /* POLARIS */ {POLARIS_DENSE_MEM, 0, POLARIS_IO_BASE}, | |
128 | /* CPUDEP */ {0, 0, 0}, /* for platforms dependent on CPU type */ | |
129 | /* PCIDEP */ {0, 0, 0}, /* for platforms dependent on core logic */ | |
a8d236a8 UD |
130 | }; |
131 | ||
132 | static struct platform { | |
133 | const char *name; | |
134 | iosys_t io_sys; | |
135 | } platform[] = { | |
136 | {"Alcor", IOSYS_CIA}, | |
137 | {"Avanti", IOSYS_APECS}, | |
a8d236a8 | 138 | {"Cabriolet", IOSYS_APECS}, |
05246799 | 139 | {"EB164", IOSYS_PCIDEP}, |
a8d236a8 UD |
140 | {"EB64+", IOSYS_APECS}, |
141 | {"EB66", IOSYS_APECS}, | |
142 | {"EB66P", IOSYS_APECS}, | |
143 | {"Jensen", IOSYS_JENSEN}, | |
194b9b3b | 144 | {"Miata", IOSYS_PYXIS}, |
a8d236a8 | 145 | {"Mikasa", IOSYS_CPUDEP}, |
052065ad | 146 | {"Nautilus", IOSYS_TSUNAMI}, |
194b9b3b RH |
147 | {"Noname", IOSYS_APECS}, |
148 | {"Noritake", IOSYS_CPUDEP}, | |
a8d236a8 | 149 | {"Rawhide", IOSYS_MCPCIA}, |
194b9b3b RH |
150 | {"Ruffian", IOSYS_PYXIS}, |
151 | {"Sable", IOSYS_CPUDEP}, | |
a8d236a8 | 152 | {"Takara", IOSYS_CIA}, |
194b9b3b RH |
153 | {"Tsunami", IOSYS_TSUNAMI}, |
154 | {"XL", IOSYS_APECS}, | |
a8d236a8 UD |
155 | }; |
156 | ||
56759be2 | 157 | struct ioswtch { |
dc37f3e5 UD |
158 | void (*sethae)(unsigned long int addr); |
159 | void (*outb)(unsigned char b, unsigned long int port); | |
160 | void (*outw)(unsigned short b, unsigned long int port); | |
161 | void (*outl)(unsigned int b, unsigned long int port); | |
162 | unsigned int (*inb)(unsigned long int port); | |
163 | unsigned int (*inw)(unsigned long int port); | |
164 | unsigned int (*inl)(unsigned long int port); | |
56759be2 RM |
165 | }; |
166 | ||
56759be2 | 167 | static struct { |
194b9b3b | 168 | unsigned long int hae_cache; |
dc37f3e5 | 169 | unsigned long int base; |
56759be2 | 170 | struct ioswtch * swp; |
dc37f3e5 UD |
171 | unsigned long int bus_memory_base; |
172 | unsigned long int sparse_bus_memory_base; | |
173 | unsigned long int io_base; | |
05246799 | 174 | ioswizzle_t swiz; |
56759be2 RM |
175 | } io; |
176 | ||
194b9b3b RH |
177 | static inline void |
178 | stb_mb(unsigned char val, unsigned long addr) | |
179 | { | |
180 | __asm__("stb %1,%0; mb" : "=m"(*(vucp)addr) : "r"(val)); | |
181 | } | |
182 | ||
183 | static inline void | |
184 | stw_mb(unsigned short val, unsigned long addr) | |
185 | { | |
186 | __asm__("stw %1,%0; mb" : "=m"(*(vusp)addr) : "r"(val)); | |
187 | } | |
188 | ||
189 | static inline void | |
190 | stl_mb(unsigned int val, unsigned long addr) | |
191 | { | |
192 | __asm__("stl %1,%0; mb" : "=m"(*(vip)addr) : "r"(val)); | |
193 | } | |
194 | ||
195 | /* No need to examine error -- sethae never fails. */ | |
196 | static inline void | |
197 | __sethae(unsigned long value) | |
198 | { | |
199 | register unsigned long r16 __asm__("$16") = value; | |
200 | register unsigned long r0 __asm__("$0") = __NR_sethae; | |
201 | __asm__ __volatile__ ("callsys" | |
202 | : "=r"(r0) | |
203 | : "0"(r0), "r" (r16) | |
204 | : inline_syscall_clobbers, "$19"); | |
205 | } | |
206 | ||
207 | extern long __pciconfig_iobase(enum __pciconfig_iobase_which __which, | |
208 | unsigned long int __bus, | |
209 | unsigned long int __dfn); | |
15999cfb | 210 | |
dc37f3e5 | 211 | static inline unsigned long int |
05246799 | 212 | port_to_cpu_addr (unsigned long int port, ioswizzle_t ioswiz, int size) |
56759be2 | 213 | { |
05246799 | 214 | if (ioswiz == IOSWIZZLE_SPARSE) |
194b9b3b | 215 | return io.base + (port << 5) + ((size - 1) << 3); |
05246799 | 216 | else if (ioswiz == IOSWIZZLE_DENSE) |
a8d236a8 | 217 | return port + io.base; |
15999cfb | 218 | else |
194b9b3b | 219 | return io.base + (port << 7) + ((size - 1) << 5); |
56759be2 RM |
220 | } |
221 | ||
ee78ea88 | 222 | static inline __attribute__((always_inline)) void |
05246799 | 223 | inline_sethae (unsigned long int addr, ioswizzle_t ioswiz) |
56759be2 | 224 | { |
05246799 | 225 | if (ioswiz == IOSWIZZLE_SPARSE) |
15999cfb | 226 | { |
dc37f3e5 | 227 | unsigned long int msb; |
15999cfb RM |
228 | |
229 | /* no need to set hae if msb is 0: */ | |
230 | msb = addr & 0xf8000000; | |
194b9b3b | 231 | if (msb && msb != io.hae_cache) |
15999cfb | 232 | { |
194b9b3b | 233 | io.hae_cache = msb; |
15999cfb | 234 | __sethae (msb); |
15999cfb | 235 | } |
56759be2 | 236 | } |
194b9b3b | 237 | else if (ioswiz == IOSWIZZLE_JENSEN) |
05246799 | 238 | { |
194b9b3b | 239 | /* HAE on the Jensen is bits 31:25 shifted right. */ |
05246799 | 240 | addr >>= 25; |
194b9b3b | 241 | if (addr != io.hae_cache) |
05246799 | 242 | { |
194b9b3b | 243 | io.hae_cache = addr; |
05246799 | 244 | __sethae (addr); |
05246799 UD |
245 | } |
246 | } | |
56759be2 RM |
247 | } |
248 | ||
56759be2 | 249 | static inline void |
05246799 | 250 | inline_outb (unsigned char b, unsigned long int port, ioswizzle_t ioswiz) |
56759be2 RM |
251 | { |
252 | unsigned int w; | |
05246799 | 253 | unsigned long int addr = port_to_cpu_addr (port, ioswiz, 1); |
56759be2 | 254 | |
f3afb0ff | 255 | asm ("insbl %2,%1,%0" : "=r" (w) : "ri" (port & 0x3), "r" (b)); |
194b9b3b | 256 | stl_mb(w, addr); |
56759be2 RM |
257 | } |
258 | ||
259 | ||
260 | static inline void | |
05246799 | 261 | inline_outw (unsigned short int b, unsigned long int port, ioswizzle_t ioswiz) |
56759be2 | 262 | { |
194b9b3b | 263 | unsigned long w; |
05246799 | 264 | unsigned long int addr = port_to_cpu_addr (port, ioswiz, 2); |
56759be2 | 265 | |
f3afb0ff | 266 | asm ("inswl %2,%1,%0" : "=r" (w) : "ri" (port & 0x3), "r" (b)); |
194b9b3b | 267 | stl_mb(w, addr); |
56759be2 RM |
268 | } |
269 | ||
270 | ||
271 | static inline void | |
05246799 | 272 | inline_outl (unsigned int b, unsigned long int port, ioswizzle_t ioswiz) |
56759be2 | 273 | { |
05246799 | 274 | unsigned long int addr = port_to_cpu_addr (port, ioswiz, 4); |
56759be2 | 275 | |
194b9b3b | 276 | stl_mb(b, addr); |
56759be2 RM |
277 | } |
278 | ||
279 | ||
280 | static inline unsigned int | |
05246799 | 281 | inline_inb (unsigned long int port, ioswizzle_t ioswiz) |
56759be2 | 282 | { |
194b9b3b RH |
283 | unsigned long int addr = port_to_cpu_addr (port, ioswiz, 1); |
284 | int result; | |
56759be2 | 285 | |
194b9b3b | 286 | result = *(vip) addr; |
56759be2 RM |
287 | result >>= (port & 3) * 8; |
288 | return 0xffUL & result; | |
289 | } | |
290 | ||
291 | ||
292 | static inline unsigned int | |
05246799 | 293 | inline_inw (unsigned long int port, ioswizzle_t ioswiz) |
56759be2 | 294 | { |
194b9b3b RH |
295 | unsigned long int addr = port_to_cpu_addr (port, ioswiz, 2); |
296 | int result; | |
56759be2 | 297 | |
194b9b3b | 298 | result = *(vip) addr; |
56759be2 RM |
299 | result >>= (port & 3) * 8; |
300 | return 0xffffUL & result; | |
301 | } | |
302 | ||
303 | ||
304 | static inline unsigned int | |
05246799 | 305 | inline_inl (unsigned long int port, ioswizzle_t ioswiz) |
56759be2 | 306 | { |
05246799 | 307 | unsigned long int addr = port_to_cpu_addr (port, ioswiz, 4); |
56759be2 | 308 | |
56759be2 RM |
309 | return *(vuip) addr; |
310 | } | |
311 | ||
a8d236a8 UD |
312 | /* |
313 | * Now define the inline functions for CPUs supporting byte/word insns, | |
314 | * and whose core logic supports I/O space accesses utilizing them. | |
315 | * | |
316 | * These routines could be used by MIATA, for example, because it has | |
05246799 UD |
317 | * and EV56 plus PYXIS, but it currently uses SPARSE anyway. This is |
318 | * also true of RX164 which used POLARIS, but we will choose to use | |
319 | * these routines in that case instead of SPARSE. | |
a8d236a8 UD |
320 | * |
321 | * These routines are necessary for TSUNAMI/TYPHOON based platforms, | |
322 | * which will have (at least) EV6. | |
323 | */ | |
324 | ||
05246799 UD |
325 | static inline unsigned long int |
326 | dense_port_to_cpu_addr (unsigned long int port) | |
327 | { | |
328 | return port + io.base; | |
329 | } | |
330 | ||
a8d236a8 | 331 | static inline void |
05246799 | 332 | inline_bwx_outb (unsigned char b, unsigned long int port) |
a8d236a8 | 333 | { |
05246799 | 334 | unsigned long int addr = dense_port_to_cpu_addr (port); |
194b9b3b | 335 | stb_mb (b, addr); |
a8d236a8 UD |
336 | } |
337 | ||
a8d236a8 | 338 | static inline void |
05246799 | 339 | inline_bwx_outw (unsigned short int b, unsigned long int port) |
a8d236a8 | 340 | { |
05246799 | 341 | unsigned long int addr = dense_port_to_cpu_addr (port); |
194b9b3b | 342 | stw_mb (b, addr); |
a8d236a8 UD |
343 | } |
344 | ||
a8d236a8 | 345 | static inline void |
05246799 | 346 | inline_bwx_outl (unsigned int b, unsigned long int port) |
a8d236a8 | 347 | { |
05246799 | 348 | unsigned long int addr = dense_port_to_cpu_addr (port); |
194b9b3b | 349 | stl_mb (b, addr); |
a8d236a8 UD |
350 | } |
351 | ||
a8d236a8 | 352 | static inline unsigned int |
05246799 | 353 | inline_bwx_inb (unsigned long int port) |
a8d236a8 | 354 | { |
194b9b3b RH |
355 | unsigned long int addr = dense_port_to_cpu_addr (port); |
356 | unsigned char r; | |
a8d236a8 | 357 | |
194b9b3b RH |
358 | __asm__ ("ldbu %0,%1" : "=r"(r) : "m"(*(vucp)addr)); |
359 | return r; | |
a8d236a8 UD |
360 | } |
361 | ||
a8d236a8 | 362 | static inline unsigned int |
05246799 | 363 | inline_bwx_inw (unsigned long int port) |
a8d236a8 | 364 | { |
194b9b3b RH |
365 | unsigned long int addr = dense_port_to_cpu_addr (port); |
366 | unsigned short r; | |
a8d236a8 | 367 | |
194b9b3b RH |
368 | __asm__ ("ldwu %0,%1" : "=r"(r) : "m"(*(vusp)addr)); |
369 | return r; | |
a8d236a8 UD |
370 | } |
371 | ||
a8d236a8 | 372 | static inline unsigned int |
05246799 | 373 | inline_bwx_inl (unsigned long int port) |
a8d236a8 | 374 | { |
05246799 | 375 | unsigned long int addr = dense_port_to_cpu_addr (port); |
a8d236a8 UD |
376 | |
377 | return *(vuip) addr; | |
378 | } | |
379 | ||
05246799 | 380 | /* macros to define routines with appropriate names and functions */ |
a8d236a8 | 381 | |
05246799 UD |
382 | /* these do either SPARSE or JENSEN swizzle */ |
383 | ||
384 | #define DCL_SETHAE(name, ioswiz) \ | |
385 | static void \ | |
386 | name##_sethae (unsigned long int addr) \ | |
387 | { \ | |
388 | inline_sethae (addr, IOSWIZZLE_##ioswiz); \ | |
56759be2 RM |
389 | } |
390 | ||
05246799 | 391 | #define DCL_OUT(name, func, type, ioswiz) \ |
56759be2 | 392 | static void \ |
dc37f3e5 | 393 | name##_##func (unsigned type b, unsigned long int addr) \ |
56759be2 | 394 | { \ |
05246799 | 395 | inline_##func (b, addr, IOSWIZZLE_##ioswiz); \ |
56759be2 RM |
396 | } |
397 | ||
05246799 | 398 | #define DCL_IN(name, func, ioswiz) \ |
56759be2 | 399 | static unsigned int \ |
dc37f3e5 | 400 | name##_##func (unsigned long int addr) \ |
56759be2 | 401 | { \ |
05246799 | 402 | return inline_##func (addr, IOSWIZZLE_##ioswiz); \ |
56759be2 RM |
403 | } |
404 | ||
05246799 | 405 | /* these do DENSE, so no swizzle is needed */ |
a8d236a8 | 406 | |
05246799 | 407 | #define DCL_OUT_BWX(name, func, type) \ |
a8d236a8 UD |
408 | static void \ |
409 | name##_##func (unsigned type b, unsigned long int addr) \ | |
410 | { \ | |
05246799 | 411 | inline_bwx_##func (b, addr); \ |
a8d236a8 UD |
412 | } |
413 | ||
05246799 | 414 | #define DCL_IN_BWX(name, func) \ |
a8d236a8 UD |
415 | static unsigned int \ |
416 | name##_##func (unsigned long int addr) \ | |
417 | { \ | |
05246799 | 418 | return inline_bwx_##func (addr); \ |
a8d236a8 UD |
419 | } |
420 | ||
05246799 | 421 | /* now declare/define the necessary routines */ |
56759be2 RM |
422 | |
423 | DCL_SETHAE(jensen, JENSEN) | |
424 | DCL_OUT(jensen, outb, char, JENSEN) | |
dc37f3e5 | 425 | DCL_OUT(jensen, outw, short int, JENSEN) |
56759be2 RM |
426 | DCL_OUT(jensen, outl, int, JENSEN) |
427 | DCL_IN(jensen, inb, JENSEN) | |
428 | DCL_IN(jensen, inw, JENSEN) | |
429 | DCL_IN(jensen, inl, JENSEN) | |
430 | ||
05246799 UD |
431 | DCL_SETHAE(sparse, SPARSE) |
432 | DCL_OUT(sparse, outb, char, SPARSE) | |
433 | DCL_OUT(sparse, outw, short int, SPARSE) | |
434 | DCL_OUT(sparse, outl, int, SPARSE) | |
435 | DCL_IN(sparse, inb, SPARSE) | |
436 | DCL_IN(sparse, inw, SPARSE) | |
437 | DCL_IN(sparse, inl, SPARSE) | |
438 | ||
194b9b3b | 439 | DCL_SETHAE(dense, DENSE) |
05246799 UD |
440 | DCL_OUT_BWX(dense, outb, char) |
441 | DCL_OUT_BWX(dense, outw, short int) | |
442 | DCL_OUT_BWX(dense, outl, int) | |
443 | DCL_IN_BWX(dense, inb) | |
444 | DCL_IN_BWX(dense, inw) | |
445 | DCL_IN_BWX(dense, inl) | |
446 | ||
447 | /* define the "swizzle" switch */ | |
a8d236a8 | 448 | static struct ioswtch ioswtch[] = { |
56759be2 RM |
449 | { |
450 | jensen_sethae, | |
451 | jensen_outb, jensen_outw, jensen_outl, | |
452 | jensen_inb, jensen_inw, jensen_inl | |
453 | }, | |
454 | { | |
05246799 UD |
455 | sparse_sethae, |
456 | sparse_outb, sparse_outw, sparse_outl, | |
457 | sparse_inb, sparse_inw, sparse_inl | |
a8d236a8 UD |
458 | }, |
459 | { | |
194b9b3b | 460 | dense_sethae, |
05246799 UD |
461 | dense_outb, dense_outw, dense_outl, |
462 | dense_inb, dense_inw, dense_inl | |
56759be2 RM |
463 | } |
464 | }; | |
465 | ||
c628e038 UD |
466 | #undef DEBUG_IOPERM |
467 | ||
194b9b3b RH |
468 | /* Routine to process the /proc/cpuinfo information into the fields |
469 | that are required for correctly determining the platform parameters. */ | |
05246799 | 470 | |
194b9b3b RH |
471 | struct cpuinfo_data |
472 | { | |
473 | char systype[256]; /* system type field */ | |
474 | char sysvari[256]; /* system variation field */ | |
475 | char cpumodel[256]; /* cpu model field */ | |
476 | }; | |
05246799 | 477 | |
194b9b3b RH |
478 | static inline int |
479 | process_cpuinfo(struct cpuinfo_data *data) | |
05246799 | 480 | { |
194b9b3b | 481 | int got_type, got_vari, got_model; |
05246799 UD |
482 | char dummy[256]; |
483 | FILE * fp; | |
194b9b3b RH |
484 | int n; |
485 | ||
486 | data->systype[0] = 0; | |
487 | data->sysvari[0] = 0; | |
488 | data->cpumodel[0] = 0; | |
489 | ||
490 | /* If there's an /etc/alpha_systype link, we're intending to override | |
491 | whatever's in /proc/cpuinfo. */ | |
492 | n = __readlink (PATH_ALPHA_SYSTYPE, data->systype, 256 - 1); | |
493 | if (n > 0) | |
494 | { | |
495 | data->systype[n] = '\0'; | |
496 | return 1; | |
497 | } | |
56759be2 | 498 | |
99449c15 | 499 | fp = fopen (PATH_CPUINFO, "rce"); |
05246799 UD |
500 | if (!fp) |
501 | return 0; | |
502 | ||
503 | got_type = got_vari = got_model = 0; | |
05246799 UD |
504 | |
505 | while (1) | |
506 | { | |
a797e173 | 507 | if (fgets_unlocked (dummy, 256, fp) == NULL) |
194b9b3b | 508 | break; |
05246799 | 509 | if (!got_type && |
194b9b3b | 510 | sscanf (dummy, "system type : %256[^\n]\n", data->systype) == 1) |
05246799 UD |
511 | got_type = 1; |
512 | if (!got_vari && | |
194b9b3b | 513 | sscanf (dummy, "system variation : %256[^\n]\n", data->sysvari) == 1) |
05246799 UD |
514 | got_vari = 1; |
515 | if (!got_model && | |
194b9b3b | 516 | sscanf (dummy, "cpu model : %256[^\n]\n", data->cpumodel) == 1) |
05246799 UD |
517 | got_model = 1; |
518 | } | |
519 | ||
520 | fclose (fp); | |
521 | ||
c628e038 | 522 | #ifdef DEBUG_IOPERM |
194b9b3b RH |
523 | fprintf(stderr, "system type: `%s'\n", data->systype); |
524 | fprintf(stderr, "system vari: `%s'\n", data->sysvari); | |
525 | fprintf(stderr, "cpu model: `%s'\n", data->cpumodel); | |
05246799 UD |
526 | #endif |
527 | ||
194b9b3b | 528 | return got_type + got_vari + got_model; |
05246799 | 529 | } |
194b9b3b RH |
530 | |
531 | ||
15999cfb | 532 | /* |
194b9b3b | 533 | * Initialize I/O system. |
15999cfb | 534 | */ |
56759be2 RM |
535 | static int |
536 | init_iosys (void) | |
537 | { | |
194b9b3b RH |
538 | long addr; |
539 | int i, olderrno = errno; | |
540 | struct cpuinfo_data data; | |
56759be2 | 541 | |
194b9b3b RH |
542 | /* First try the pciconfig_iobase syscall added to 2.2.15 and 2.3.99. */ |
543 | ||
9790568c | 544 | #ifdef __NR_pciconfig_iobase |
194b9b3b RH |
545 | addr = __pciconfig_iobase (IOBASE_DENSE_MEM, 0, 0); |
546 | if (addr != -1) | |
15999cfb | 547 | { |
194b9b3b RH |
548 | ioswizzle_t io_swiz; |
549 | ||
550 | if (addr == 0) | |
551 | { | |
552 | /* Only Jensen doesn't have dense mem space. */ | |
553 | io.sparse_bus_memory_base | |
554 | = io_system[IOSYS_JENSEN].sparse_bus_mem_base; | |
555 | io.io_base = io_system[IOSYS_JENSEN].bus_io_base; | |
556 | io_swiz = IOSWIZZLE_JENSEN; | |
557 | } | |
558 | else | |
dc37f3e5 | 559 | { |
194b9b3b RH |
560 | io.bus_memory_base = addr; |
561 | ||
562 | addr = __pciconfig_iobase (IOBASE_DENSE_IO, 0, 0); | |
563 | if (addr != 0) | |
564 | { | |
565 | /* The X server uses _bus_base_sparse == 0 to know that | |
566 | BWX access are supported to dense mem space. This is | |
567 | true of every system that supports dense io space, so | |
568 | never fill in io.sparse_bus_memory_base in this case. */ | |
569 | io_swiz = IOSWIZZLE_DENSE; | |
570 | io.io_base = addr; | |
571 | } | |
572 | else | |
dc37f3e5 | 573 | { |
194b9b3b RH |
574 | io.sparse_bus_memory_base |
575 | = __pciconfig_iobase (IOBASE_SPARSE_MEM, 0, 0); | |
576 | io.io_base = __pciconfig_iobase (IOBASE_SPARSE_IO, 0, 0); | |
577 | io_swiz = IOSWIZZLE_SPARSE; | |
dc37f3e5 | 578 | } |
dc37f3e5 | 579 | } |
194b9b3b RH |
580 | |
581 | io.swiz = io_swiz; | |
582 | io.swp = &ioswtch[io_swiz]; | |
583 | ||
584 | return 0; | |
15999cfb | 585 | } |
9790568c | 586 | #endif |
15999cfb | 587 | |
194b9b3b RH |
588 | /* Second, collect the contents of /etc/alpha_systype or /proc/cpuinfo. */ |
589 | ||
590 | if (process_cpuinfo(&data) == 0) | |
591 | { | |
592 | /* This can happen if the format of /proc/cpuinfo changes. */ | |
593 | fprintf (stderr, | |
594 | "ioperm.init_iosys: Unable to determine system type.\n" | |
595 | "\t(May need " PATH_ALPHA_SYSTYPE " symlink?)\n"); | |
596 | __set_errno (ENODEV); | |
597 | return -1; | |
15999cfb | 598 | } |
56759be2 | 599 | |
194b9b3b | 600 | /* Translate systype name into i/o system. */ |
15999cfb RM |
601 | for (i = 0; i < sizeof (platform) / sizeof (platform[0]); ++i) |
602 | { | |
194b9b3b | 603 | if (strcmp (platform[i].name, data.systype) == 0) |
15999cfb | 604 | { |
194b9b3b RH |
605 | iosys_t io_sys = platform[i].io_sys; |
606 | ||
607 | /* Some platforms can have either EV4 or EV5 CPUs. */ | |
608 | if (io_sys == IOSYS_CPUDEP) | |
a8d236a8 | 609 | { |
194b9b3b | 610 | /* SABLE or MIKASA or NORITAKE so far. */ |
a8d236a8 UD |
611 | if (strcmp (platform[i].name, "Sable") == 0) |
612 | { | |
194b9b3b RH |
613 | if (strncmp (data.cpumodel, "EV4", 3) == 0) |
614 | io_sys = IOSYS_T2; | |
615 | else if (strncmp (data.cpumodel, "EV5", 3) == 0) | |
616 | io_sys = IOSYS_GAMMA; | |
a8d236a8 UD |
617 | } |
618 | else | |
194b9b3b RH |
619 | { |
620 | /* This covers MIKASA/NORITAKE. */ | |
621 | if (strncmp (data.cpumodel, "EV4", 3) == 0) | |
622 | io_sys = IOSYS_APECS; | |
623 | else if (strncmp (data.cpumodel, "EV5", 3) == 0) | |
624 | io_sys = IOSYS_CIA; | |
a8d236a8 | 625 | } |
194b9b3b | 626 | if (io_sys == IOSYS_CPUDEP) |
a8d236a8 UD |
627 | { |
628 | /* This can happen if the format of /proc/cpuinfo changes.*/ | |
194b9b3b | 629 | fprintf (stderr, "ioperm.init_iosys: Unable to determine" |
a8d236a8 UD |
630 | " CPU model.\n"); |
631 | __set_errno (ENODEV); | |
632 | return -1; | |
633 | } | |
634 | } | |
194b9b3b RH |
635 | /* Some platforms can have different core logic chipsets */ |
636 | if (io_sys == IOSYS_PCIDEP) | |
05246799 | 637 | { |
194b9b3b RH |
638 | /* EB164 so far */ |
639 | if (strcmp (data.systype, "EB164") == 0) | |
05246799 | 640 | { |
194b9b3b RH |
641 | if (strncmp (data.sysvari, "RX164", 5) == 0) |
642 | io_sys = IOSYS_POLARIS; | |
643 | else if (strncmp (data.sysvari, "LX164", 5) == 0 | |
644 | || strncmp (data.sysvari, "SX164", 5) == 0) | |
645 | io_sys = IOSYS_PYXIS; | |
05246799 | 646 | else |
194b9b3b | 647 | io_sys = IOSYS_CIA; |
05246799 | 648 | } |
194b9b3b | 649 | if (io_sys == IOSYS_PCIDEP) |
05246799 UD |
650 | { |
651 | /* This can happen if the format of /proc/cpuinfo changes.*/ | |
194b9b3b | 652 | fprintf (stderr, "ioperm.init_iosys: Unable to determine" |
05246799 UD |
653 | " core logic chipset.\n"); |
654 | __set_errno (ENODEV); | |
655 | return -1; | |
656 | } | |
657 | } | |
194b9b3b RH |
658 | io.bus_memory_base = io_system[io_sys].bus_memory_base; |
659 | io.sparse_bus_memory_base = io_system[io_sys].sparse_bus_mem_base; | |
660 | io.io_base = io_system[io_sys].bus_io_base; | |
a8d236a8 | 661 | |
194b9b3b | 662 | if (io_sys == IOSYS_JENSEN) |
05246799 | 663 | io.swiz = IOSWIZZLE_JENSEN; |
194b9b3b RH |
664 | else if (io_sys == IOSYS_TSUNAMI |
665 | || io_sys == IOSYS_POLARIS | |
666 | || io_sys == IOSYS_PYXIS) | |
05246799 | 667 | io.swiz = IOSWIZZLE_DENSE; |
56759be2 | 668 | else |
05246799 UD |
669 | io.swiz = IOSWIZZLE_SPARSE; |
670 | io.swp = &ioswtch[io.swiz]; | |
194b9b3b RH |
671 | |
672 | __set_errno (olderrno); | |
56759be2 RM |
673 | return 0; |
674 | } | |
56759be2 | 675 | } |
15999cfb | 676 | |
194b9b3b RH |
677 | __set_errno (ENODEV); |
678 | fprintf(stderr, "ioperm.init_iosys: Platform not recognized.\n" | |
679 | "\t(May need " PATH_ALPHA_SYSTYPE " symlink?)\n"); | |
56759be2 RM |
680 | return -1; |
681 | } | |
682 | ||
683 | ||
684 | int | |
dc37f3e5 | 685 | _ioperm (unsigned long int from, unsigned long int num, int turn_on) |
56759be2 | 686 | { |
194b9b3b RH |
687 | unsigned long int addr, len, pagesize = __getpagesize(); |
688 | int prot; | |
56759be2 | 689 | |
194b9b3b RH |
690 | if (!io.swp && init_iosys() < 0) |
691 | { | |
c628e038 | 692 | #ifdef DEBUG_IOPERM |
194b9b3b | 693 | fprintf(stderr, "ioperm: init_iosys() failed (%m)\n"); |
05246799 | 694 | #endif |
194b9b3b RH |
695 | return -1; |
696 | } | |
56759be2 | 697 | |
194b9b3b | 698 | /* This test isn't as silly as it may look like; consider overflows! */ |
15999cfb RM |
699 | if (from >= MAX_PORT || from + num > MAX_PORT) |
700 | { | |
1120c0ed | 701 | __set_errno (EINVAL); |
c628e038 | 702 | #ifdef DEBUG_IOPERM |
05246799 UD |
703 | fprintf(stderr, "ioperm: from/num out of range\n"); |
704 | #endif | |
15999cfb RM |
705 | return -1; |
706 | } | |
56759be2 | 707 | |
c628e038 | 708 | #ifdef DEBUG_IOPERM |
194b9b3b | 709 | fprintf(stderr, "ioperm: turn_on %d io.base %ld\n", turn_on, io.base); |
05246799 UD |
710 | #endif |
711 | ||
15999cfb RM |
712 | if (turn_on) |
713 | { | |
714 | if (!io.base) | |
715 | { | |
15999cfb RM |
716 | int fd; |
717 | ||
194b9b3b | 718 | io.hae_cache = 0; |
05246799 | 719 | if (io.swiz != IOSWIZZLE_DENSE) |
194b9b3b RH |
720 | { |
721 | /* Synchronize with hw. */ | |
722 | __sethae (0); | |
723 | } | |
15999cfb | 724 | |
194b9b3b RH |
725 | fd = __open ("/dev/mem", O_RDWR); |
726 | if (fd < 0) | |
727 | { | |
c628e038 | 728 | #ifdef DEBUG_IOPERM |
194b9b3b | 729 | fprintf(stderr, "ioperm: /dev/mem open failed (%m)\n"); |
05246799 | 730 | #endif |
194b9b3b RH |
731 | return -1; |
732 | } | |
15999cfb | 733 | |
05246799 UD |
734 | addr = port_to_cpu_addr (0, io.swiz, 1); |
735 | len = port_to_cpu_addr (MAX_PORT, io.swiz, 1) - addr; | |
15999cfb | 736 | io.base = |
dc37f3e5 | 737 | (unsigned long int) __mmap (0, len, PROT_NONE, MAP_SHARED, |
a8d236a8 | 738 | fd, io.io_base); |
194b9b3b | 739 | __close (fd); |
c628e038 | 740 | #ifdef DEBUG_IOPERM |
05246799 UD |
741 | fprintf(stderr, "ioperm: mmap of len 0x%lx returned 0x%lx\n", |
742 | len, io.base); | |
743 | #endif | |
15999cfb RM |
744 | if ((long) io.base == -1) |
745 | return -1; | |
746 | } | |
747 | prot = PROT_READ | PROT_WRITE; | |
56759be2 | 748 | } |
15999cfb RM |
749 | else |
750 | { | |
751 | if (!io.base) | |
752 | return 0; /* never was turned on... */ | |
56759be2 | 753 | |
15999cfb RM |
754 | /* turnoff access to relevant pages: */ |
755 | prot = PROT_NONE; | |
756 | } | |
05246799 | 757 | addr = port_to_cpu_addr (from, io.swiz, 1); |
194b9b3b | 758 | addr &= ~(pagesize - 1); |
05246799 | 759 | len = port_to_cpu_addr (from + num, io.swiz, 1) - addr; |
194b9b3b | 760 | return __mprotect ((void *) addr, len, prot); |
56759be2 RM |
761 | } |
762 | ||
763 | ||
764 | int | |
194b9b3b RH |
765 | _iopl (int level) |
766 | { | |
767 | switch (level) | |
768 | { | |
769 | case 0: | |
770 | return 0; | |
771 | ||
772 | case 1: case 2: case 3: | |
773 | return _ioperm (0, MAX_PORT, 1); | |
774 | ||
775 | default: | |
776 | __set_errno (EINVAL); | |
777 | return -1; | |
778 | } | |
56759be2 RM |
779 | } |
780 | ||
781 | ||
782 | void | |
dc37f3e5 | 783 | _sethae (unsigned long int addr) |
56759be2 RM |
784 | { |
785 | if (!io.swp && init_iosys () < 0) | |
786 | return; | |
787 | ||
788 | io.swp->sethae (addr); | |
789 | } | |
790 | ||
791 | ||
792 | void | |
dc37f3e5 | 793 | _outb (unsigned char b, unsigned long int port) |
56759be2 RM |
794 | { |
795 | if (port >= MAX_PORT) | |
796 | return; | |
797 | ||
798 | io.swp->outb (b, port); | |
799 | } | |
800 | ||
801 | ||
802 | void | |
dc37f3e5 | 803 | _outw (unsigned short b, unsigned long int port) |
56759be2 RM |
804 | { |
805 | if (port >= MAX_PORT) | |
806 | return; | |
807 | ||
808 | io.swp->outw (b, port); | |
809 | } | |
810 | ||
811 | ||
812 | void | |
dc37f3e5 | 813 | _outl (unsigned int b, unsigned long int port) |
56759be2 RM |
814 | { |
815 | if (port >= MAX_PORT) | |
816 | return; | |
817 | ||
818 | io.swp->outl (b, port); | |
819 | } | |
820 | ||
821 | ||
822 | unsigned int | |
dc37f3e5 | 823 | _inb (unsigned long int port) |
56759be2 RM |
824 | { |
825 | return io.swp->inb (port); | |
826 | } | |
827 | ||
828 | ||
829 | unsigned int | |
dc37f3e5 | 830 | _inw (unsigned long int port) |
56759be2 RM |
831 | { |
832 | return io.swp->inw (port); | |
833 | } | |
834 | ||
835 | ||
836 | unsigned int | |
dc37f3e5 | 837 | _inl (unsigned long int port) |
56759be2 RM |
838 | { |
839 | return io.swp->inl (port); | |
840 | } | |
841 | ||
842 | ||
dc37f3e5 | 843 | unsigned long int |
15999cfb RM |
844 | _bus_base(void) |
845 | { | |
846 | if (!io.swp && init_iosys () < 0) | |
847 | return -1; | |
11309adf | 848 | return io.bus_memory_base; |
15999cfb RM |
849 | } |
850 | ||
dc37f3e5 | 851 | unsigned long int |
dca26bcb UD |
852 | _bus_base_sparse(void) |
853 | { | |
854 | if (!io.swp && init_iosys () < 0) | |
855 | return -1; | |
11309adf UD |
856 | return io.sparse_bus_memory_base; |
857 | } | |
858 | ||
859 | int | |
860 | _hae_shift(void) | |
861 | { | |
862 | if (!io.swp && init_iosys () < 0) | |
863 | return -1; | |
194b9b3b RH |
864 | if (io.swiz == IOSWIZZLE_JENSEN) |
865 | return 7; | |
866 | if (io.swiz == IOSWIZZLE_SPARSE) | |
867 | return 5; | |
868 | return 0; | |
dca26bcb UD |
869 | } |
870 | ||
56759be2 RM |
871 | weak_alias (_sethae, sethae); |
872 | weak_alias (_ioperm, ioperm); | |
873 | weak_alias (_iopl, iopl); | |
874 | weak_alias (_inb, inb); | |
875 | weak_alias (_inw, inw); | |
876 | weak_alias (_inl, inl); | |
877 | weak_alias (_outb, outb); | |
878 | weak_alias (_outw, outw); | |
879 | weak_alias (_outl, outl); | |
15999cfb | 880 | weak_alias (_bus_base, bus_base); |
dca26bcb | 881 | weak_alias (_bus_base_sparse, bus_base_sparse); |
11309adf | 882 | weak_alias (_hae_shift, hae_shift); |