[MIPS] R4000/R4400 daddiu erratum workaround
[linux-2.6] / arch / mips / lib / memset.S
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) 1998, 1999, 2000 by Ralf Baechle
7  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
8  * Copyright (C) 2007  Maciej W. Rozycki
9  */
10 #include <asm/asm.h>
11 #include <asm/asm-offsets.h>
12 #include <asm/regdef.h>
13
14 #if LONGSIZE == 4
15 #define LONG_S_L swl
16 #define LONG_S_R swr
17 #else
18 #define LONG_S_L sdl
19 #define LONG_S_R sdr
20 #endif
21
22 #define EX(insn,reg,addr,handler)                       \
23 9:      insn    reg, addr;                              \
24         .section __ex_table,"a";                        \
25         PTR     9b, handler;                            \
26         .previous
27
28         .macro  f_fill64 dst, offset, val, fixup
29         EX(LONG_S, \val, (\offset +  0 * LONGSIZE)(\dst), \fixup)
30         EX(LONG_S, \val, (\offset +  1 * LONGSIZE)(\dst), \fixup)
31         EX(LONG_S, \val, (\offset +  2 * LONGSIZE)(\dst), \fixup)
32         EX(LONG_S, \val, (\offset +  3 * LONGSIZE)(\dst), \fixup)
33         EX(LONG_S, \val, (\offset +  4 * LONGSIZE)(\dst), \fixup)
34         EX(LONG_S, \val, (\offset +  5 * LONGSIZE)(\dst), \fixup)
35         EX(LONG_S, \val, (\offset +  6 * LONGSIZE)(\dst), \fixup)
36         EX(LONG_S, \val, (\offset +  7 * LONGSIZE)(\dst), \fixup)
37 #if LONGSIZE == 4
38         EX(LONG_S, \val, (\offset +  8 * LONGSIZE)(\dst), \fixup)
39         EX(LONG_S, \val, (\offset +  9 * LONGSIZE)(\dst), \fixup)
40         EX(LONG_S, \val, (\offset + 10 * LONGSIZE)(\dst), \fixup)
41         EX(LONG_S, \val, (\offset + 11 * LONGSIZE)(\dst), \fixup)
42         EX(LONG_S, \val, (\offset + 12 * LONGSIZE)(\dst), \fixup)
43         EX(LONG_S, \val, (\offset + 13 * LONGSIZE)(\dst), \fixup)
44         EX(LONG_S, \val, (\offset + 14 * LONGSIZE)(\dst), \fixup)
45         EX(LONG_S, \val, (\offset + 15 * LONGSIZE)(\dst), \fixup)
46 #endif
47         .endm
48
49 /*
50  * memset(void *s, int c, size_t n)
51  *
52  * a0: start of area to clear
53  * a1: char to fill with
54  * a2: size of area to clear
55  */
56         .set    noreorder
57         .align  5
58 LEAF(memset)
59         beqz            a1, 1f
60          move           v0, a0                  /* result */
61
62         andi            a1, 0xff                /* spread fillword */
63         LONG_SLL                t1, a1, 8
64         or              a1, t1
65         LONG_SLL                t1, a1, 16
66 #if LONGSIZE == 8
67         or              a1, t1
68         LONG_SLL                t1, a1, 32
69 #endif
70         or              a1, t1
71 1:
72
73 FEXPORT(__bzero)
74         sltiu           t0, a2, LONGSIZE        /* very small region? */
75         bnez            t0, small_memset
76          andi           t0, a0, LONGMASK        /* aligned? */
77
78 #ifndef CONFIG_CPU_DADDI_WORKAROUNDS
79         beqz            t0, 1f
80          PTR_SUBU       t0, LONGSIZE            /* alignment in bytes */
81 #else
82         .set            noat
83         li              AT, LONGSIZE
84         beqz            t0, 1f
85          PTR_SUBU       t0, AT                  /* alignment in bytes */
86         .set            at
87 #endif
88
89 #ifdef __MIPSEB__
90         EX(LONG_S_L, a1, (a0), first_fixup)     /* make word/dword aligned */
91 #endif
92 #ifdef __MIPSEL__
93         EX(LONG_S_R, a1, (a0), first_fixup)     /* make word/dword aligned */
94 #endif
95         PTR_SUBU        a0, t0                  /* long align ptr */
96         PTR_ADDU        a2, t0                  /* correct size */
97
98 1:      ori             t1, a2, 0x3f            /* # of full blocks */
99         xori            t1, 0x3f
100         beqz            t1, memset_partial      /* no block to fill */
101          andi           t0, a2, 0x40-LONGSIZE
102
103         PTR_ADDU        t1, a0                  /* end address */
104         .set            reorder
105 1:      PTR_ADDIU       a0, 64
106         f_fill64 a0, -64, a1, fwd_fixup
107         bne             t1, a0, 1b
108         .set            noreorder
109
110 memset_partial:
111         PTR_LA          t1, 2f                  /* where to start */
112 #if LONGSIZE == 4
113         PTR_SUBU        t1, t0
114 #else
115         .set            noat
116         LONG_SRL                AT, t0, 1
117         PTR_SUBU        t1, AT
118         .set            at
119 #endif
120         jr              t1
121          PTR_ADDU       a0, t0                  /* dest ptr */
122
123         .set            push
124         .set            noreorder
125         .set            nomacro
126         f_fill64 a0, -64, a1, partial_fixup     /* ... but first do longs ... */
127 2:      .set            pop
128         andi            a2, LONGMASK            /* At most one long to go */
129
130         beqz            a2, 1f
131          PTR_ADDU       a0, a2                  /* What's left */
132 #ifdef __MIPSEB__
133         EX(LONG_S_R, a1, -1(a0), last_fixup)
134 #endif
135 #ifdef __MIPSEL__
136         EX(LONG_S_L, a1, -1(a0), last_fixup)
137 #endif
138 1:      jr              ra
139          move           a2, zero
140
141 small_memset:
142         beqz            a2, 2f
143          PTR_ADDU       t1, a0, a2
144
145 1:      PTR_ADDIU       a0, 1                   /* fill bytewise */
146         bne             t1, a0, 1b
147          sb             a1, -1(a0)
148
149 2:      jr              ra                      /* done */
150          move           a2, zero
151         END(memset)
152
153 first_fixup:
154         jr      ra
155          nop
156
157 fwd_fixup:
158         PTR_L           t0, TI_TASK($28)
159         LONG_L          t0, THREAD_BUADDR(t0)
160         andi            a2, 0x3f
161         LONG_ADDU       a2, t1
162         jr              ra
163          LONG_SUBU      a2, t0
164
165 partial_fixup:
166         PTR_L           t0, TI_TASK($28)
167         LONG_L          t0, THREAD_BUADDR(t0)
168         andi            a2, LONGMASK
169         LONG_ADDU       a2, t1
170         jr              ra
171          LONG_SUBU      a2, t0
172
173 last_fixup:
174         jr              ra
175          andi           v1, a2, LONGMASK