File: /usr/src/linux/arch/alpha/lib/io.c

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