File: /usr/src/linux/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 extern 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 extern 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 extern 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 extern 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 extern 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 extern 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 */
160