Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Alpha IO and memory functions. | |
3 | */ | |
4 | ||
5 | #include <linux/kernel.h> | |
6 | #include <linux/types.h> | |
7 | #include <linux/string.h> | |
8 | #include <linux/module.h> | |
9 | #include <asm/io.h> | |
10 | ||
11 | /* Out-of-line versions of the i/o routines that redirect into the | |
12 | platform-specific version. Note that "platform-specific" may mean | |
13 | "generic", which bumps through the machine vector. */ | |
14 | ||
15 | unsigned int | |
16 | ioread8(void __iomem *addr) | |
17 | { | |
18 | unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread8)(addr); | |
19 | mb(); | |
20 | return ret; | |
21 | } | |
22 | ||
23 | unsigned int ioread16(void __iomem *addr) | |
24 | { | |
25 | unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread16)(addr); | |
26 | mb(); | |
27 | return ret; | |
28 | } | |
29 | ||
30 | unsigned int ioread32(void __iomem *addr) | |
31 | { | |
32 | unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread32)(addr); | |
33 | mb(); | |
34 | return ret; | |
35 | } | |
36 | ||
37 | void iowrite8(u8 b, void __iomem *addr) | |
38 | { | |
39 | IO_CONCAT(__IO_PREFIX,iowrite8)(b, addr); | |
40 | mb(); | |
41 | } | |
42 | ||
43 | void iowrite16(u16 b, void __iomem *addr) | |
44 | { | |
45 | IO_CONCAT(__IO_PREFIX,iowrite16)(b, addr); | |
46 | mb(); | |
47 | } | |
48 | ||
49 | void iowrite32(u32 b, void __iomem *addr) | |
50 | { | |
51 | IO_CONCAT(__IO_PREFIX,iowrite32)(b, addr); | |
52 | mb(); | |
53 | } | |
54 | ||
55 | EXPORT_SYMBOL(ioread8); | |
56 | EXPORT_SYMBOL(ioread16); | |
57 | EXPORT_SYMBOL(ioread32); | |
58 | EXPORT_SYMBOL(iowrite8); | |
59 | EXPORT_SYMBOL(iowrite16); | |
60 | EXPORT_SYMBOL(iowrite32); | |
61 | ||
62 | u8 inb(unsigned long port) | |
63 | { | |
64 | return ioread8(ioport_map(port, 1)); | |
65 | } | |
66 | ||
67 | u16 inw(unsigned long port) | |
68 | { | |
69 | return ioread16(ioport_map(port, 2)); | |
70 | } | |
71 | ||
72 | u32 inl(unsigned long port) | |
73 | { | |
74 | return ioread32(ioport_map(port, 4)); | |
75 | } | |
76 | ||
77 | void outb(u8 b, unsigned long port) | |
78 | { | |
79 | iowrite8(b, ioport_map(port, 1)); | |
80 | } | |
81 | ||
82 | void outw(u16 b, unsigned long port) | |
83 | { | |
84 | iowrite16(b, ioport_map(port, 2)); | |
85 | } | |
86 | ||
87 | void outl(u32 b, unsigned long port) | |
88 | { | |
89 | iowrite32(b, ioport_map(port, 4)); | |
90 | } | |
91 | ||
92 | EXPORT_SYMBOL(inb); | |
93 | EXPORT_SYMBOL(inw); | |
94 | EXPORT_SYMBOL(inl); | |
95 | EXPORT_SYMBOL(outb); | |
96 | EXPORT_SYMBOL(outw); | |
97 | EXPORT_SYMBOL(outl); | |
98 | ||
99 | u8 __raw_readb(const volatile void __iomem *addr) | |
100 | { | |
101 | return IO_CONCAT(__IO_PREFIX,readb)(addr); | |
102 | } | |
103 | ||
104 | u16 __raw_readw(const volatile void __iomem *addr) | |
105 | { | |
106 | return IO_CONCAT(__IO_PREFIX,readw)(addr); | |
107 | } | |
108 | ||
109 | u32 __raw_readl(const volatile void __iomem *addr) | |
110 | { | |
111 | return IO_CONCAT(__IO_PREFIX,readl)(addr); | |
112 | } | |
113 | ||
114 | u64 __raw_readq(const volatile void __iomem *addr) | |
115 | { | |
116 | return IO_CONCAT(__IO_PREFIX,readq)(addr); | |
117 | } | |
118 | ||
119 | void __raw_writeb(u8 b, volatile void __iomem *addr) | |
120 | { | |
121 | IO_CONCAT(__IO_PREFIX,writeb)(b, addr); | |
122 | } | |
123 | ||
124 | void __raw_writew(u16 b, volatile void __iomem *addr) | |
125 | { | |
126 | IO_CONCAT(__IO_PREFIX,writew)(b, addr); | |
127 | } | |
128 | ||
129 | void __raw_writel(u32 b, volatile void __iomem *addr) | |
130 | { | |
131 | IO_CONCAT(__IO_PREFIX,writel)(b, addr); | |
132 | } | |
133 | ||
134 | void __raw_writeq(u64 b, volatile void __iomem *addr) | |
135 | { | |
136 | IO_CONCAT(__IO_PREFIX,writeq)(b, addr); | |
137 | } | |
138 | ||
139 | EXPORT_SYMBOL(__raw_readb); | |
140 | EXPORT_SYMBOL(__raw_readw); | |
141 | EXPORT_SYMBOL(__raw_readl); | |
142 | EXPORT_SYMBOL(__raw_readq); | |
143 | EXPORT_SYMBOL(__raw_writeb); | |
144 | EXPORT_SYMBOL(__raw_writew); | |
145 | EXPORT_SYMBOL(__raw_writel); | |
146 | EXPORT_SYMBOL(__raw_writeq); | |
147 | ||
148 | u8 readb(const volatile void __iomem *addr) | |
149 | { | |
150 | u8 ret = __raw_readb(addr); | |
151 | mb(); | |
152 | return ret; | |
153 | } | |
154 | ||
155 | u16 readw(const volatile void __iomem *addr) | |
156 | { | |
157 | u16 ret = __raw_readw(addr); | |
158 | mb(); | |
159 | return ret; | |
160 | } | |
161 | ||
162 | u32 readl(const volatile void __iomem *addr) | |
163 | { | |
164 | u32 ret = __raw_readl(addr); | |
165 | mb(); | |
166 | return ret; | |
167 | } | |
168 | ||
169 | u64 readq(const volatile void __iomem *addr) | |
170 | { | |
171 | u64 ret = __raw_readq(addr); | |
172 | mb(); | |
173 | return ret; | |
174 | } | |
175 | ||
176 | void writeb(u8 b, volatile void __iomem *addr) | |
177 | { | |
178 | __raw_writeb(b, addr); | |
179 | mb(); | |
180 | } | |
181 | ||
182 | void writew(u16 b, volatile void __iomem *addr) | |
183 | { | |
184 | __raw_writew(b, addr); | |
185 | mb(); | |
186 | } | |
187 | ||
188 | void writel(u32 b, volatile void __iomem *addr) | |
189 | { | |
190 | __raw_writel(b, addr); | |
191 | mb(); | |
192 | } | |
193 | ||
194 | void writeq(u64 b, volatile void __iomem *addr) | |
195 | { | |
196 | __raw_writeq(b, addr); | |
197 | mb(); | |
198 | } | |
199 | ||
200 | EXPORT_SYMBOL(readb); | |
201 | EXPORT_SYMBOL(readw); | |
202 | EXPORT_SYMBOL(readl); | |
203 | EXPORT_SYMBOL(readq); | |
204 | EXPORT_SYMBOL(writeb); | |
205 | EXPORT_SYMBOL(writew); | |
206 | EXPORT_SYMBOL(writel); | |
207 | EXPORT_SYMBOL(writeq); | |
208 | ||
209 | ||
210 | /* | |
211 | * Read COUNT 8-bit bytes from port PORT into memory starting at SRC. | |
212 | */ | |
213 | void ioread8_rep(void __iomem *port, void *dst, unsigned long count) | |
214 | { | |
215 | while ((unsigned long)dst & 0x3) { | |
216 | if (!count) | |
217 | return; | |
218 | count--; | |
219 | *(unsigned char *)dst = ioread8(port); | |
220 | dst += 1; | |
221 | } | |
222 | ||
223 | while (count >= 4) { | |
224 | unsigned int w; | |
225 | count -= 4; | |
226 | w = ioread8(port); | |
227 | w |= ioread8(port) << 8; | |
228 | w |= ioread8(port) << 16; | |
229 | w |= ioread8(port) << 24; | |
230 | *(unsigned int *)dst = w; | |
231 | dst += 4; | |
232 | } | |
233 | ||
234 | while (count) { | |
235 | --count; | |
236 | *(unsigned char *)dst = ioread8(port); | |
237 | dst += 1; | |
238 | } | |
239 | } | |
240 | ||
241 | void insb(unsigned long port, void *dst, unsigned long count) | |
242 | { | |
243 | ioread8_rep(ioport_map(port, 1), dst, count); | |
244 | } | |
245 | ||
246 | EXPORT_SYMBOL(ioread8_rep); | |
247 | EXPORT_SYMBOL(insb); | |
248 | ||
249 | /* | |
250 | * Read COUNT 16-bit words from port PORT into memory starting at | |
251 | * SRC. SRC must be at least short aligned. This is used by the | |
252 | * IDE driver to read disk sectors. Performance is important, but | |
253 | * the interfaces seems to be slow: just using the inlined version | |
254 | * of the inw() breaks things. | |
255 | */ | |
256 | void ioread16_rep(void __iomem *port, void *dst, unsigned long count) | |
257 | { | |
258 | if (unlikely((unsigned long)dst & 0x3)) { | |
259 | if (!count) | |
260 | return; | |
261 | BUG_ON((unsigned long)dst & 0x1); | |
262 | count--; | |
263 | *(unsigned short *)dst = ioread16(port); | |
264 | dst += 2; | |
265 | } | |
266 | ||
267 | while (count >= 2) { | |
268 | unsigned int w; | |
269 | count -= 2; | |
270 | w = ioread16(port); | |
271 | w |= ioread16(port) << 16; | |
272 | *(unsigned int *)dst = w; | |
273 | dst += 4; | |
274 | } | |
275 | ||
276 | if (count) { | |
277 | *(unsigned short*)dst = ioread16(port); | |
278 | } | |
279 | } | |
280 | ||
281 | void insw(unsigned long port, void *dst, unsigned long count) | |
282 | { | |
283 | ioread16_rep(ioport_map(port, 2), dst, count); | |
284 | } | |
285 | ||
286 | EXPORT_SYMBOL(ioread16_rep); | |
287 | EXPORT_SYMBOL(insw); | |
288 | ||
289 | ||
290 | /* | |
291 | * Read COUNT 32-bit words from port PORT into memory starting at | |
292 | * SRC. Now works with any alignment in SRC. Performance is important, | |
293 | * but the interfaces seems to be slow: just using the inlined version | |
294 | * of the inl() breaks things. | |
295 | */ | |
296 | void ioread32_rep(void __iomem *port, void *dst, unsigned long count) | |
297 | { | |
298 | if (unlikely((unsigned long)dst & 0x3)) { | |
299 | while (count--) { | |
300 | struct S { int x __attribute__((packed)); }; | |
301 | ((struct S *)dst)->x = ioread32(port); | |
302 | dst += 4; | |
303 | } | |
304 | } else { | |
305 | /* Buffer 32-bit aligned. */ | |
306 | while (count--) { | |
307 | *(unsigned int *)dst = ioread32(port); | |
308 | dst += 4; | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | void insl(unsigned long port, void *dst, unsigned long count) | |
314 | { | |
315 | ioread32_rep(ioport_map(port, 4), dst, count); | |
316 | } | |
317 | ||
318 | EXPORT_SYMBOL(ioread32_rep); | |
319 | EXPORT_SYMBOL(insl); | |
320 | ||
321 | ||
322 | /* | |
323 | * Like insb but in the opposite direction. | |
324 | * Don't worry as much about doing aligned memory transfers: | |
325 | * doing byte reads the "slow" way isn't nearly as slow as | |
326 | * doing byte writes the slow way (no r-m-w cycle). | |
327 | */ | |
328 | void iowrite8_rep(void __iomem *port, const void *xsrc, unsigned long count) | |
329 | { | |
330 | const unsigned char *src = xsrc; | |
331 | while (count--) | |
332 | iowrite8(*src++, port); | |
333 | } | |
334 | ||
335 | void outsb(unsigned long port, const void *src, unsigned long count) | |
336 | { | |
337 | iowrite8_rep(ioport_map(port, 1), src, count); | |
338 | } | |
339 | ||
340 | EXPORT_SYMBOL(iowrite8_rep); | |
341 | EXPORT_SYMBOL(outsb); | |
342 | ||
343 | ||
344 | /* | |
345 | * Like insw but in the opposite direction. This is used by the IDE | |
346 | * driver to write disk sectors. Performance is important, but the | |
347 | * interfaces seems to be slow: just using the inlined version of the | |
348 | * outw() breaks things. | |
349 | */ | |
350 | void iowrite16_rep(void __iomem *port, const void *src, unsigned long count) | |
351 | { | |
352 | if (unlikely((unsigned long)src & 0x3)) { | |
353 | if (!count) | |
354 | return; | |
355 | BUG_ON((unsigned long)src & 0x1); | |
356 | iowrite16(*(unsigned short *)src, port); | |
357 | src += 2; | |
358 | --count; | |
359 | } | |
360 | ||
361 | while (count >= 2) { | |
362 | unsigned int w; | |
363 | count -= 2; | |
364 | w = *(unsigned int *)src; | |
365 | src += 4; | |
366 | iowrite16(w >> 0, port); | |
367 | iowrite16(w >> 16, port); | |
368 | } | |
369 | ||
370 | if (count) { | |
371 | iowrite16(*(unsigned short *)src, port); | |
372 | } | |
373 | } | |
374 | ||
375 | void outsw(unsigned long port, const void *src, unsigned long count) | |
376 | { | |
377 | iowrite16_rep(ioport_map(port, 2), src, count); | |
378 | } | |
379 | ||
380 | EXPORT_SYMBOL(iowrite16_rep); | |
381 | EXPORT_SYMBOL(outsw); | |
382 | ||
383 | ||
384 | /* | |
385 | * Like insl but in the opposite direction. This is used by the IDE | |
386 | * driver to write disk sectors. Works with any alignment in SRC. | |
387 | * Performance is important, but the interfaces seems to be slow: | |
388 | * just using the inlined version of the outl() breaks things. | |
389 | */ | |
390 | void iowrite32_rep(void __iomem *port, const void *src, unsigned long count) | |
391 | { | |
392 | if (unlikely((unsigned long)src & 0x3)) { | |
393 | while (count--) { | |
394 | struct S { int x __attribute__((packed)); }; | |
395 | iowrite32(((struct S *)src)->x, port); | |
396 | src += 4; | |
397 | } | |
398 | } else { | |
399 | /* Buffer 32-bit aligned. */ | |
400 | while (count--) { | |
401 | iowrite32(*(unsigned int *)src, port); | |
402 | src += 4; | |
403 | } | |
404 | } | |
405 | } | |
406 | ||
407 | void outsl(unsigned long port, const void *src, unsigned long count) | |
408 | { | |
409 | iowrite32_rep(ioport_map(port, 4), src, count); | |
410 | } | |
411 | ||
412 | EXPORT_SYMBOL(iowrite32_rep); | |
413 | EXPORT_SYMBOL(outsl); | |
414 | ||
415 | ||
416 | /* | |
417 | * Copy data from IO memory space to "real" memory space. | |
418 | * This needs to be optimized. | |
419 | */ | |
420 | void memcpy_fromio(void *to, const volatile void __iomem *from, long count) | |
421 | { | |
422 | /* Optimize co-aligned transfers. Everything else gets handled | |
423 | a byte at a time. */ | |
424 | ||
425 | if (count >= 8 && ((u64)to & 7) == ((u64)from & 7)) { | |
426 | count -= 8; | |
427 | do { | |
428 | *(u64 *)to = __raw_readq(from); | |
429 | count -= 8; | |
430 | to += 8; | |
431 | from += 8; | |
432 | } while (count >= 0); | |
433 | count += 8; | |
434 | } | |
435 | ||
436 | if (count >= 4 && ((u64)to & 3) == ((u64)from & 3)) { | |
437 | count -= 4; | |
438 | do { | |
439 | *(u32 *)to = __raw_readl(from); | |
440 | count -= 4; | |
441 | to += 4; | |
442 | from += 4; | |
443 | } while (count >= 0); | |
444 | count += 4; | |
445 | } | |
446 | ||
447 | if (count >= 2 && ((u64)to & 1) == ((u64)from & 1)) { | |
448 | count -= 2; | |
449 | do { | |
450 | *(u16 *)to = __raw_readw(from); | |
451 | count -= 2; | |
452 | to += 2; | |
453 | from += 2; | |
454 | } while (count >= 0); | |
455 | count += 2; | |
456 | } | |
457 | ||
458 | while (count > 0) { | |
459 | *(u8 *) to = __raw_readb(from); | |
460 | count--; | |
461 | to++; | |
462 | from++; | |
463 | } | |
464 | mb(); | |
465 | } | |
466 | ||
467 | EXPORT_SYMBOL(memcpy_fromio); | |
468 | ||
469 | ||
470 | /* | |
471 | * Copy data from "real" memory space to IO memory space. | |
472 | * This needs to be optimized. | |
473 | */ | |
474 | void memcpy_toio(volatile void __iomem *to, const void *from, long count) | |
475 | { | |
476 | /* Optimize co-aligned transfers. Everything else gets handled | |
477 | a byte at a time. */ | |
478 | /* FIXME -- align FROM. */ | |
479 | ||
480 | if (count >= 8 && ((u64)to & 7) == ((u64)from & 7)) { | |
481 | count -= 8; | |
482 | do { | |
483 | __raw_writeq(*(const u64 *)from, to); | |
484 | count -= 8; | |
485 | to += 8; | |
486 | from += 8; | |
487 | } while (count >= 0); | |
488 | count += 8; | |
489 | } | |
490 | ||
491 | if (count >= 4 && ((u64)to & 3) == ((u64)from & 3)) { | |
492 | count -= 4; | |
493 | do { | |
494 | __raw_writel(*(const u32 *)from, to); | |
495 | count -= 4; | |
496 | to += 4; | |
497 | from += 4; | |
498 | } while (count >= 0); | |
499 | count += 4; | |
500 | } | |
501 | ||
502 | if (count >= 2 && ((u64)to & 1) == ((u64)from & 1)) { | |
503 | count -= 2; | |
504 | do { | |
505 | __raw_writew(*(const u16 *)from, to); | |
506 | count -= 2; | |
507 | to += 2; | |
508 | from += 2; | |
509 | } while (count >= 0); | |
510 | count += 2; | |
511 | } | |
512 | ||
513 | while (count > 0) { | |
514 | __raw_writeb(*(const u8 *) from, to); | |
515 | count--; | |
516 | to++; | |
517 | from++; | |
518 | } | |
519 | mb(); | |
520 | } | |
521 | ||
522 | EXPORT_SYMBOL(memcpy_toio); | |
523 | ||
524 | ||
525 | /* | |
526 | * "memset" on IO memory space. | |
527 | */ | |
528 | void _memset_c_io(volatile void __iomem *to, unsigned long c, long count) | |
529 | { | |
530 | /* Handle any initial odd byte */ | |
531 | if (count > 0 && ((u64)to & 1)) { | |
532 | __raw_writeb(c, to); | |
533 | to++; | |
534 | count--; | |
535 | } | |
536 | ||
537 | /* Handle any initial odd halfword */ | |
538 | if (count >= 2 && ((u64)to & 2)) { | |
539 | __raw_writew(c, to); | |
540 | to += 2; | |
541 | count -= 2; | |
542 | } | |
543 | ||
544 | /* Handle any initial odd word */ | |
545 | if (count >= 4 && ((u64)to & 4)) { | |
546 | __raw_writel(c, to); | |
547 | to += 4; | |
548 | count -= 4; | |
549 | } | |
550 | ||
551 | /* Handle all full-sized quadwords: we're aligned | |
552 | (or have a small count) */ | |
553 | count -= 8; | |
554 | if (count >= 0) { | |
555 | do { | |
556 | __raw_writeq(c, to); | |
557 | to += 8; | |
558 | count -= 8; | |
559 | } while (count >= 0); | |
560 | } | |
561 | count += 8; | |
562 | ||
563 | /* The tail is word-aligned if we still have count >= 4 */ | |
564 | if (count >= 4) { | |
565 | __raw_writel(c, to); | |
566 | to += 4; | |
567 | count -= 4; | |
568 | } | |
569 | ||
570 | /* The tail is half-word aligned if we have count >= 2 */ | |
571 | if (count >= 2) { | |
572 | __raw_writew(c, to); | |
573 | to += 2; | |
574 | count -= 2; | |
575 | } | |
576 | ||
577 | /* And finally, one last byte.. */ | |
578 | if (count) { | |
579 | __raw_writeb(c, to); | |
580 | } | |
581 | mb(); | |
582 | } | |
583 | ||
584 | EXPORT_SYMBOL(_memset_c_io); | |
585 | ||
586 | /* A version of memcpy used by the vga console routines to move data around | |
587 | arbitrarily between screen and main memory. */ | |
588 | ||
589 | void | |
590 | scr_memcpyw(u16 *d, const u16 *s, unsigned int count) | |
591 | { | |
592 | const u16 __iomem *ios = (const u16 __iomem *) s; | |
593 | u16 __iomem *iod = (u16 __iomem *) d; | |
594 | int s_isio = __is_ioaddr(s); | |
595 | int d_isio = __is_ioaddr(d); | |
596 | ||
597 | if (s_isio) { | |
598 | if (d_isio) { | |
599 | /* FIXME: Should handle unaligned ops and | |
600 | operation widening. */ | |
601 | ||
602 | count /= 2; | |
603 | while (count--) { | |
604 | u16 tmp = __raw_readw(ios++); | |
605 | __raw_writew(tmp, iod++); | |
606 | } | |
607 | } | |
608 | else | |
609 | memcpy_fromio(d, ios, count); | |
610 | } else { | |
611 | if (d_isio) | |
612 | memcpy_toio(iod, s, count); | |
613 | else | |
614 | memcpy(d, s, count); | |
615 | } | |
616 | } | |
617 | ||
618 | EXPORT_SYMBOL(scr_memcpyw); | |
619 | ||
620 | void __iomem *ioport_map(unsigned long port, unsigned int size) | |
621 | { | |
622 | return IO_CONCAT(__IO_PREFIX,ioportmap) (port); | |
623 | } | |
624 | ||
625 | void ioport_unmap(void __iomem *addr) | |
626 | { | |
627 | } | |
628 | ||
629 | EXPORT_SYMBOL(ioport_map); | |
630 | EXPORT_SYMBOL(ioport_unmap); |