make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / include / asm-mips / unaligned.h
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1996, 1999, 2000 by Ralf Baechle
7  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
8  */
9 #ifndef _ASM_UNALIGNED_H
10 #define _ASM_UNALIGNED_H
11
12 extern void __get_unaligned_bad_length(void);
13 extern void __put_unaligned_bad_length(void);
14
15 /*
16  * Load double unaligned.
17  *
18  * This could have been implemented in plain C like IA64 but egcs 1.0.3a
19  * inflates this to 23 instructions ...
20  */
21 static inline unsigned long long __ldq_u(const unsigned long long * __addr)
22 {
23         unsigned long long __res;
24
25         __asm__("ulw\t%0, %1\n\t"
26                 "ulw\t%D0, 4+%1"
27                 : "=&r" (__res)
28                 : "m" (*__addr));
29
30         return __res;
31 }
32
33 /*
34  * Load word unaligned.
35  */
36 static inline unsigned long __ldl_u(const unsigned int * __addr)
37 {
38         unsigned long __res;
39
40         __asm__("ulw\t%0,%1"
41                 : "=&r" (__res)
42                 : "m" (*__addr));
43
44         return __res;
45 }
46
47 /*
48  * Load halfword unaligned.
49  */
50 static inline unsigned long __ldw_u(const unsigned short * __addr)
51 {
52         unsigned long __res;
53
54         __asm__("ulh\t%0,%1"
55                 : "=&r" (__res)
56                 : "m" (*__addr));
57
58         return __res;
59 }
60
61 /*
62  * Store doubleword ununaligned.
63  */
64 static inline void __stq_u(unsigned long __val, unsigned long long * __addr)
65 {
66         __asm__("usw\t%1, %0\n\t"
67                 "usw\t%D1, 4+%0"
68                 : "=m" (*__addr)
69                 : "r" (__val));
70 }
71
72 /*
73  * Store long ununaligned.
74  */
75 static inline void __stl_u(unsigned long __val, unsigned int * __addr)
76 {
77         __asm__("usw\t%1, %0"
78                 : "=m" (*__addr)
79                 : "r" (__val));
80 }
81
82 /*
83  * Store word ununaligned.
84  */
85 static inline void __stw_u(unsigned long __val, unsigned short * __addr)
86 {
87         __asm__("ush\t%1, %0"
88                 : "=m" (*__addr)
89                 : "r" (__val));
90 }
91
92 /*
93  * get_unaligned - get value from possibly mis-aligned location
94  * @ptr: pointer to value
95  *
96  * This macro should be used for accessing values larger in size than
97  * single bytes at locations that are expected to be improperly aligned,
98  * e.g. retrieving a u16 value from a location not u16-aligned.
99  *
100  * Note that unaligned accesses can be very expensive on some architectures.
101  */
102 #define get_unaligned(ptr)                                              \
103 ({                                                                      \
104         __typeof__(*(ptr)) __val;                                       \
105                                                                         \
106         switch (sizeof(*(ptr))) {                                       \
107         case 1:                                                         \
108                 __val = *(const unsigned char *)ptr;                    \
109                 break;                                                  \
110         case 2:                                                         \
111                 __val = __ldw_u((const unsigned short *)ptr);           \
112                 break;                                                  \
113         case 4:                                                         \
114                 __val = __ldl_u((const unsigned int *)ptr);             \
115                 break;                                                  \
116         case 8:                                                         \
117                 __val = __ldq_u((const unsigned long long *)ptr);       \
118                 break;                                                  \
119         default:                                                        \
120                 __get_unaligned_bad_length();                           \
121                 break;                                                  \
122         }                                                               \
123                                                                         \
124         __val;                                                          \
125 })
126
127 /*
128  * put_unaligned - put value to a possibly mis-aligned location
129  * @val: value to place
130  * @ptr: pointer to location
131  *
132  * This macro should be used for placing values larger in size than
133  * single bytes at locations that are expected to be improperly aligned,
134  * e.g. writing a u16 value to a location not u16-aligned.
135  *
136  * Note that unaligned accesses can be very expensive on some architectures.
137  */
138 #define put_unaligned(val,ptr)                                          \
139 do {                                                                    \
140         switch (sizeof(*(ptr))) {                                       \
141         case 1:                                                         \
142                 *(unsigned char *)(ptr) = (val);                        \
143                 break;                                                  \
144         case 2:                                                         \
145                 __stw_u(val, (unsigned short *)(ptr));                  \
146                 break;                                                  \
147         case 4:                                                         \
148                 __stl_u(val, (unsigned int *)(ptr));                    \
149                 break;                                                  \
150         case 8:                                                         \
151                 __stq_u(val, (unsigned long long *)(ptr));              \
152                 break;                                                  \
153         default:                                                        \
154                 __put_unaligned_bad_length();                           \
155                 break;                                                  \
156         }                                                               \
157 } while(0)
158
159 #endif /* _ASM_UNALIGNED_H */