]>
Commit | Line | Data |
---|---|---|
35832e26 MSJ |
1 | /* |
2 | * SMP/VPE-safe functions to access "registers" (see note). | |
3 | * | |
4 | * NOTES: | |
5 | * - These macros use ll/sc instructions, so it is your responsibility to | |
6 | * ensure these are available on your platform before including this file. | |
7 | * - The MIPS32 spec states that ll/sc results are undefined for uncached | |
8 | * accesses. This means they can't be used on HW registers accessed | |
9 | * through kseg1. Code which requires these macros for this purpose must | |
10 | * front-end the registers with cached memory "registers" and have a single | |
11 | * thread update the actual HW registers. | |
12 | * - A maximum of 2k of code can be inserted between ll and sc. Every | |
13 | * memory accesses between the instructions will increase the chance of | |
14 | * sc failing and having to loop. | |
15 | * - When using custom_read_reg32/custom_write_reg32 only perform the | |
16 | * necessary logical operations on the register value in between these | |
17 | * two calls. All other logic should be performed before the first call. | |
18 | * - There is a bug on the R10000 chips which has a workaround. If you | |
19 | * are affected by this bug, make sure to define the symbol 'R10000_LLSC_WAR' | |
20 | * to be non-zero. If you are using this header from within linux, you may | |
21 | * include <asm/war.h> before including this file to have this defined | |
22 | * appropriately for you. | |
23 | * | |
24 | * Copyright 2005-2007 PMC-Sierra, Inc. | |
25 | * | |
26 | * This program is free software; you can redistribute it and/or modify it | |
27 | * under the terms of the GNU General Public License as published by the | |
28 | * Free Software Foundation; either version 2 of the License, or (at your | |
29 | * option) any later version. | |
30 | * | |
31 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
32 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
33 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
34 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
35 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
36 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
37 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
38 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
39 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
40 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
41 | * | |
42 | * You should have received a copy of the GNU General Public License along | |
43 | * with this program; if not, write to the Free Software Foundation, Inc., 675 | |
44 | * Mass Ave, Cambridge, MA 02139, USA. | |
45 | */ | |
46 | ||
47 | #ifndef __ASM_REGOPS_H__ | |
48 | #define __ASM_REGOPS_H__ | |
49 | ||
50 | #include <linux/types.h> | |
51 | ||
52 | #include <asm/war.h> | |
53 | ||
54 | #ifndef R10000_LLSC_WAR | |
55 | #define R10000_LLSC_WAR 0 | |
56 | #endif | |
57 | ||
58 | #if R10000_LLSC_WAR == 1 | |
59 | #define __beqz "beqzl " | |
60 | #else | |
61 | #define __beqz "beqz " | |
62 | #endif | |
63 | ||
64 | #ifndef _LINUX_TYPES_H | |
65 | typedef unsigned int u32; | |
66 | #endif | |
67 | ||
68 | /* | |
69 | * Sets all the masked bits to the corresponding value bits | |
70 | */ | |
71 | static inline void set_value_reg32(volatile u32 *const addr, | |
72 | u32 const mask, | |
73 | u32 const value) | |
74 | { | |
75 | u32 temp; | |
76 | ||
77 | __asm__ __volatile__( | |
78 | " .set push \n" | |
79 | " .set mips3 \n" | |
80 | "1: ll %0, %1 # set_value_reg32 \n" | |
81 | " and %0, %2 \n" | |
82 | " or %0, %3 \n" | |
83 | " sc %0, %1 \n" | |
84 | " "__beqz"%0, 1b \n" | |
85 | " nop \n" | |
86 | " .set pop \n" | |
87 | : "=&r" (temp), "=m" (*addr) | |
88 | : "ir" (~mask), "ir" (value), "m" (*addr)); | |
89 | } | |
90 | ||
91 | /* | |
92 | * Sets all the masked bits to '1' | |
93 | */ | |
94 | static inline void set_reg32(volatile u32 *const addr, | |
95 | u32 const mask) | |
96 | { | |
97 | u32 temp; | |
98 | ||
99 | __asm__ __volatile__( | |
100 | " .set push \n" | |
101 | " .set mips3 \n" | |
102 | "1: ll %0, %1 # set_reg32 \n" | |
103 | " or %0, %2 \n" | |
104 | " sc %0, %1 \n" | |
105 | " "__beqz"%0, 1b \n" | |
106 | " nop \n" | |
107 | " .set pop \n" | |
108 | : "=&r" (temp), "=m" (*addr) | |
109 | : "ir" (mask), "m" (*addr)); | |
110 | } | |
111 | ||
112 | /* | |
113 | * Sets all the masked bits to '0' | |
114 | */ | |
115 | static inline void clear_reg32(volatile u32 *const addr, | |
116 | u32 const mask) | |
117 | { | |
118 | u32 temp; | |
119 | ||
120 | __asm__ __volatile__( | |
121 | " .set push \n" | |
122 | " .set mips3 \n" | |
123 | "1: ll %0, %1 # clear_reg32 \n" | |
124 | " and %0, %2 \n" | |
125 | " sc %0, %1 \n" | |
126 | " "__beqz"%0, 1b \n" | |
127 | " nop \n" | |
128 | " .set pop \n" | |
129 | : "=&r" (temp), "=m" (*addr) | |
130 | : "ir" (~mask), "m" (*addr)); | |
131 | } | |
132 | ||
133 | /* | |
134 | * Toggles all masked bits from '0' to '1' and '1' to '0' | |
135 | */ | |
136 | static inline void toggle_reg32(volatile u32 *const addr, | |
137 | u32 const mask) | |
138 | { | |
139 | u32 temp; | |
140 | ||
141 | __asm__ __volatile__( | |
142 | " .set push \n" | |
143 | " .set mips3 \n" | |
144 | "1: ll %0, %1 # toggle_reg32 \n" | |
145 | " xor %0, %2 \n" | |
146 | " sc %0, %1 \n" | |
147 | " "__beqz"%0, 1b \n" | |
148 | " nop \n" | |
149 | " .set pop \n" | |
150 | : "=&r" (temp), "=m" (*addr) | |
151 | : "ir" (mask), "m" (*addr)); | |
152 | } | |
153 | ||
154 | /* | |
155 | * Read all masked bits others are returned as '0' | |
156 | */ | |
157 | static inline u32 read_reg32(volatile u32 *const addr, | |
158 | u32 const mask) | |
159 | { | |
160 | u32 temp; | |
161 | ||
162 | __asm__ __volatile__( | |
163 | " .set push \n" | |
164 | " .set noreorder \n" | |
165 | " lw %0, %1 # read \n" | |
166 | " and %0, %2 # mask \n" | |
167 | " .set pop \n" | |
168 | : "=&r" (temp) | |
169 | : "m" (*addr), "ir" (mask)); | |
170 | ||
171 | return temp; | |
172 | } | |
173 | ||
174 | /* | |
175 | * blocking_read_reg32 - Read address with blocking load | |
176 | * | |
177 | * Uncached writes need to be read back to ensure they reach RAM. | |
178 | * The returned value must be 'used' to prevent from becoming a | |
179 | * non-blocking load. | |
180 | */ | |
181 | static inline u32 blocking_read_reg32(volatile u32 *const addr) | |
182 | { | |
183 | u32 temp; | |
184 | ||
185 | __asm__ __volatile__( | |
186 | " .set push \n" | |
187 | " .set noreorder \n" | |
188 | " lw %0, %1 # read \n" | |
189 | " move %0, %0 # block \n" | |
190 | " .set pop \n" | |
191 | : "=&r" (temp) | |
192 | : "m" (*addr)); | |
193 | ||
194 | return temp; | |
195 | } | |
196 | ||
197 | /* | |
198 | * For special strange cases only: | |
199 | * | |
200 | * If you need custom processing within a ll/sc loop, use the following macros | |
201 | * VERY CAREFULLY: | |
202 | * | |
203 | * u32 tmp; <-- Define a variable to hold the data | |
204 | * | |
205 | * custom_read_reg32(address, tmp); <-- Reads the address and put the value | |
206 | * in the 'tmp' variable given | |
207 | * | |
25985edc | 208 | * From here on out, you are (basically) atomic, so don't do anything too |
35832e26 MSJ |
209 | * fancy! |
210 | * Also, this code may loop if the end of this block fails to write | |
211 | * everything back safely due do the other CPU, so do NOT do anything | |
212 | * with side-effects! | |
213 | * | |
214 | * custom_write_reg32(address, tmp); <-- Writes back 'tmp' safely. | |
215 | */ | |
216 | #define custom_read_reg32(address, tmp) \ | |
217 | __asm__ __volatile__( \ | |
218 | " .set push \n" \ | |
219 | " .set mips3 \n" \ | |
220 | "1: ll %0, %1 #custom_read_reg32 \n" \ | |
221 | " .set pop \n" \ | |
222 | : "=r" (tmp), "=m" (*address) \ | |
223 | : "m" (*address)) | |
224 | ||
225 | #define custom_write_reg32(address, tmp) \ | |
226 | __asm__ __volatile__( \ | |
227 | " .set push \n" \ | |
228 | " .set mips3 \n" \ | |
229 | " sc %0, %1 #custom_write_reg32 \n" \ | |
230 | " "__beqz"%0, 1b \n" \ | |
231 | " nop \n" \ | |
232 | " .set pop \n" \ | |
233 | : "=&r" (tmp), "=m" (*address) \ | |
234 | : "0" (tmp), "m" (*address)) | |
235 | ||
70342287 | 236 | #endif /* __ASM_REGOPS_H__ */ |