diff options
Diffstat (limited to 'arch/blackfin/lib')
-rw-r--r-- | arch/blackfin/lib/Makefile | 11 | ||||
-rw-r--r-- | arch/blackfin/lib/ashldi3.c | 58 | ||||
-rw-r--r-- | arch/blackfin/lib/ashrdi3.c | 59 | ||||
-rw-r--r-- | arch/blackfin/lib/checksum.c | 140 | ||||
-rw-r--r-- | arch/blackfin/lib/divsi3.S | 216 | ||||
-rw-r--r-- | arch/blackfin/lib/gcclib.h | 47 | ||||
-rw-r--r-- | arch/blackfin/lib/ins.S | 69 | ||||
-rw-r--r-- | arch/blackfin/lib/lshrdi3.c | 72 | ||||
-rw-r--r-- | arch/blackfin/lib/memchr.S | 70 | ||||
-rw-r--r-- | arch/blackfin/lib/memcmp.S | 110 | ||||
-rw-r--r-- | arch/blackfin/lib/memcpy.S | 142 | ||||
-rw-r--r-- | arch/blackfin/lib/memmove.S | 103 | ||||
-rw-r--r-- | arch/blackfin/lib/memset.S | 109 | ||||
-rw-r--r-- | arch/blackfin/lib/modsi3.S | 79 | ||||
-rw-r--r-- | arch/blackfin/lib/muldi3.c | 99 | ||||
-rw-r--r-- | arch/blackfin/lib/outs.S | 62 | ||||
-rw-r--r-- | arch/blackfin/lib/smulsi3_highpart.S | 30 | ||||
-rw-r--r-- | arch/blackfin/lib/strcmp.c | 11 | ||||
-rw-r--r-- | arch/blackfin/lib/strcpy.c | 11 | ||||
-rw-r--r-- | arch/blackfin/lib/strncmp.c | 11 | ||||
-rw-r--r-- | arch/blackfin/lib/strncpy.c | 11 | ||||
-rw-r--r-- | arch/blackfin/lib/udivsi3.S | 298 | ||||
-rw-r--r-- | arch/blackfin/lib/umodsi3.S | 66 | ||||
-rw-r--r-- | arch/blackfin/lib/umulsi3_highpart.S | 23 |
24 files changed, 1907 insertions, 0 deletions
diff --git a/arch/blackfin/lib/Makefile b/arch/blackfin/lib/Makefile new file mode 100644 index 000000000000..635288fc5f54 --- /dev/null +++ b/arch/blackfin/lib/Makefile @@ -0,0 +1,11 @@ +# +# arch/blackfin/lib/Makefile +# + +lib-y := \ + ashldi3.o ashrdi3.o lshrdi3.o \ + muldi3.o divsi3.o udivsi3.o modsi3.o umodsi3.o \ + checksum.o memcpy.o memset.o memcmp.o memchr.o memmove.o \ + strcmp.o strcpy.o strncmp.o strncpy.o \ + umulsi3_highpart.o smulsi3_highpart.o \ + ins.o outs.o diff --git a/arch/blackfin/lib/ashldi3.c b/arch/blackfin/lib/ashldi3.c new file mode 100644 index 000000000000..a8c279e9b192 --- /dev/null +++ b/arch/blackfin/lib/ashldi3.c @@ -0,0 +1,58 @@ +/* + * File: arch/blackfin/lib/ashldi3.c + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gcclib.h" + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +DItype __ashldi3(DItype u, word_type b)__attribute__((l1_text)); +#endif + +DItype __ashldi3(DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + w.s.low = 0; + w.s.high = (USItype) uu.s.low << -bm; + } else { + USItype carries = (USItype) uu.s.low >> bm; + w.s.low = (USItype) uu.s.low << b; + w.s.high = ((USItype) uu.s.high << b) | carries; + } + + return w.ll; +} diff --git a/arch/blackfin/lib/ashrdi3.c b/arch/blackfin/lib/ashrdi3.c new file mode 100644 index 000000000000..a0d3419329ca --- /dev/null +++ b/arch/blackfin/lib/ashrdi3.c @@ -0,0 +1,59 @@ +/* + * File: arch/blackfin/lib/ashrdi3.c + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gcclib.h" + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +DItype __ashrdi3(DItype u, word_type b)__attribute__((l1_text)); +#endif + +DItype __ashrdi3(DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (sizeof(SItype) * BITS_PER_UNIT - 1); + w.s.low = uu.s.high >> -bm; + } else { + USItype carries = (USItype) uu.s.high << bm; + w.s.high = uu.s.high >> b; + w.s.low = ((USItype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/blackfin/lib/checksum.c b/arch/blackfin/lib/checksum.c new file mode 100644 index 000000000000..42768e0c80ca --- /dev/null +++ b/arch/blackfin/lib/checksum.c @@ -0,0 +1,140 @@ +/* + * File: arch/blackfin/lib/checksum.c + * Based on: none - original work + * Author: + * + * Created: + * Description: An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <net/checksum.h> +#include <asm/checksum.h> + +#ifdef CONFIG_IP_CHECKSUM_L1 +static unsigned short do_csum(const unsigned char *buff, int len)__attribute__((l1_text)); +#endif + +static unsigned short do_csum(const unsigned char *buff, int len) +{ + register unsigned long sum = 0; + int swappem = 0; + + if (1 & (unsigned long)buff) { + sum = *buff << 8; + buff++; + len--; + ++swappem; + } + + while (len > 1) { + sum += *(unsigned short *)buff; + buff += 2; + len -= 2; + } + + if (len > 0) + sum += *buff; + + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + if (swappem) + sum = ((sum & 0xff00) >> 8) + ((sum & 0x00ff) << 8); + + return sum; + +} + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + */ +unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl) +{ + return ~do_csum(iph, ihl * 4); +} + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum) +{ + /* + * Just in case we get nasty checksum data... + * Like 0xffff6ec3 in the case of our IPv6 multicast header. + * We fold to begin with, as well as at the end. + */ + sum = (sum & 0xffff) + (sum >> 16); + + sum += do_csum(buff, len); + + sum = (sum & 0xffff) + (sum >> 16); + + return sum; +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ +unsigned short ip_compute_csum(const unsigned char *buff, int len) +{ + return ~do_csum(buff, len); +} + +/* + * copy from fs while checksumming, otherwise like csum_partial + */ + +unsigned int +csum_partial_copy_from_user(const unsigned char *src, unsigned char *dst, + int len, int sum, int *csum_err) +{ + if (csum_err) + *csum_err = 0; + memcpy(dst, src, len); + return csum_partial(dst, len, sum); +} + +/* + * copy from ds while checksumming, otherwise like csum_partial + */ + +unsigned int csum_partial_copy(const unsigned char *src, unsigned char *dst, + int len, int sum) +{ + memcpy(dst, src, len); + return csum_partial(dst, len, sum); +} diff --git a/arch/blackfin/lib/divsi3.S b/arch/blackfin/lib/divsi3.S new file mode 100644 index 000000000000..3e29861852b2 --- /dev/null +++ b/arch/blackfin/lib/divsi3.S @@ -0,0 +1,216 @@ +/* + * File: arch/blackfin/lib/divsi3.S + * Based on: + * Author: + * + * Created: + * Description: 16 / 32 bit signed division. + * Special cases : + * 1) If(numerator == 0) + * return 0 + * 2) If(denominator ==0) + * return positive max = 0x7fffffff + * 3) If(numerator == denominator) + * return 1 + * 4) If(denominator ==1) + * return numerator + * 5) If(denominator == -1) + * return -numerator + * + * Operand : R0 - Numerator (i) + * R1 - Denominator (i) + * R0 - Quotient (o) + * Registers Used : R2-R7,P0-P2 + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +.global ___divsi3; + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +.section .l1.text +#else +.text +#endif + +.align 2; +___divsi3 : + + + R3 = R0 ^ R1; + R0 = ABS R0; + + CC = V; + + r3 = rot r3 by -1; + r1 = abs r1; /* now both positive, r3.30 means "negate result", + ** r3.31 means overflow, add one to result + */ + cc = r0 < r1; + if cc jump .Lret_zero; + r2 = r1 >> 15; + cc = r2; + if cc jump .Lidents; + r2 = r1 << 16; + cc = r2 <= r0; + if cc jump .Lidents; + + DIVS(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + DIVQ(R0, R1); + + R0 = R0.L (Z); + r1 = r3 >> 31; /* add overflow issue back in */ + r0 = r0 + r1; + r1 = -r0; + cc = bittst(r3, 30); + if cc r0 = r1; + RTS; + +/* Can't use the primitives. Test common identities. +** If the identity is true, return the value in R2. +*/ + +.Lidents: + CC = R1 == 0; /* check for divide by zero */ + IF CC JUMP .Lident_return; + + CC = R0 == 0; /* check for division of zero */ + IF CC JUMP .Lzero_return; + + CC = R0 == R1; /* check for identical operands */ + IF CC JUMP .Lident_return; + + CC = R1 == 1; /* check for divide by 1 */ + IF CC JUMP .Lident_return; + + R2.L = ONES R1; + R2 = R2.L (Z); + CC = R2 == 1; + IF CC JUMP .Lpower_of_two; + + /* Identities haven't helped either. + ** Perform the full division process. + */ + + P1 = 31; /* Set loop counter */ + + [--SP] = (R7:5); /* Push registers R5-R7 */ + R2 = -R1; + [--SP] = R2; + R2 = R0 << 1; /* R2 lsw of dividend */ + R6 = R0 ^ R1; /* Get sign */ + R5 = R6 >> 31; /* Shift sign to LSB */ + + R0 = 0 ; /* Clear msw partial remainder */ + R2 = R2 | R5; /* Shift quotient bit */ + R6 = R0 ^ R1; /* Get new quotient bit */ + + LSETUP(.Llst,.Llend) LC0 = P1; /* Setup loop */ +.Llst: R7 = R2 >> 31; /* record copy of carry from R2 */ + R2 = R2 << 1; /* Shift 64 bit dividend up by 1 bit */ + R0 = R0 << 1 || R5 = [SP]; + R0 = R0 | R7; /* and add carry */ + CC = R6 < 0; /* Check quotient(AQ) */ + /* we might be subtracting divisor (AQ==0) */ + IF CC R5 = R1; /* or we might be adding divisor (AQ==1)*/ + R0 = R0 + R5; /* do add or subtract, as indicated by AQ */ + R6 = R0 ^ R1; /* Generate next quotient bit */ + R5 = R6 >> 31; + /* Assume AQ==1, shift in zero */ + BITTGL(R5,0); /* tweak AQ to be what we want to shift in */ +.Llend: R2 = R2 + R5; /* and then set shifted-in value to + ** tweaked AQ. + */ + r1 = r3 >> 31; + r2 = r2 + r1; + cc = bittst(r3,30); + r0 = -r2; + if !cc r0 = r2; + SP += 4; + (R7:5)= [SP++]; /* Pop registers R6-R7 */ + RTS; + +.Lident_return: + CC = R1 == 0; /* check for divide by zero => 0x7fffffff */ + R2 = -1 (X); + R2 >>= 1; + IF CC JUMP .Ltrue_ident_return; + + CC = R0 == R1; /* check for identical operands => 1 */ + R2 = 1 (Z); + IF CC JUMP .Ltrue_ident_return; + + R2 = R0; /* assume divide by 1 => numerator */ + /*FALLTHRU*/ + +.Ltrue_ident_return: + R0 = R2; /* Return an identity value */ + R2 = -R2; + CC = bittst(R3,30); + IF CC R0 = R2; +.Lzero_return: + RTS; /* ...including zero */ + +.Lpower_of_two: + /* Y has a single bit set, which means it's a power of two. + ** That means we can perform the division just by shifting + ** X to the right the appropriate number of bits + */ + + /* signbits returns the number of sign bits, minus one. + ** 1=>30, 2=>29, ..., 0x40000000=>0. Which means we need + ** to shift right n-signbits spaces. It also means 0x80000000 + ** is a special case, because that *also* gives a signbits of 0 + */ + + R2 = R0 >> 31; + CC = R1 < 0; + IF CC JUMP .Ltrue_ident_return; + + R1.l = SIGNBITS R1; + R1 = R1.L (Z); + R1 += -30; + R0 = LSHIFT R0 by R1.L; + r1 = r3 >> 31; + r0 = r0 + r1; + R2 = -R0; // negate result if necessary + CC = bittst(R3,30); + IF CC R0 = R2; + RTS; + +.Lret_zero: + R0 = 0; + RTS; diff --git a/arch/blackfin/lib/gcclib.h b/arch/blackfin/lib/gcclib.h new file mode 100644 index 000000000000..9ccd39a135ee --- /dev/null +++ b/arch/blackfin/lib/gcclib.h @@ -0,0 +1,47 @@ +/* + * File: arch/blackfin/lib/gcclib.h + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define BITS_PER_UNIT 8 +#define SI_TYPE_SIZE (sizeof (SItype) * BITS_PER_UNIT) + +typedef unsigned int UQItype __attribute__ ((mode(QI))); +typedef int SItype __attribute__ ((mode(SI))); +typedef unsigned int USItype __attribute__ ((mode(SI))); +typedef int DItype __attribute__ ((mode(DI))); +typedef int word_type __attribute__ ((mode(__word__))); +typedef unsigned int UDItype __attribute__ ((mode(DI))); + +struct DIstruct { + SItype low, high; +}; + +typedef union { + struct DIstruct s; + DItype ll; +} DIunion; diff --git a/arch/blackfin/lib/ins.S b/arch/blackfin/lib/ins.S new file mode 100644 index 000000000000..730d2b427538 --- /dev/null +++ b/arch/blackfin/lib/ins.S @@ -0,0 +1,69 @@ +/* + * File: arch/blackfin/lib/ins.S + * Based on: + * Author: Bas Vermeulen <bas@buyways.nl> + * + * Created: Tue Mar 22 15:27:24 CEST 2005 + * Description: Implementation of ins{bwl} for BlackFin processors using zero overhead loops. + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * Copyright (C) 2005 Bas Vermeulen, BuyWays BV <bas@buyways.nl> + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +.align 2 + +ENTRY(_insl) + P0 = R0; /* P0 = port */ + cli R3; + P1 = R1; /* P1 = address */ + P2 = R2; /* P2 = count */ + SSYNC; + LSETUP( .Llong_loop_s, .Llong_loop_e) LC0 = P2; +.Llong_loop_s: R0 = [P0]; +.Llong_loop_e: [P1++] = R0; + sti R3; + RTS; + +ENTRY(_insw) + P0 = R0; /* P0 = port */ + cli R3; + P1 = R1; /* P1 = address */ + P2 = R2; /* P2 = count */ + SSYNC; + LSETUP( .Lword_loop_s, .Lword_loop_e) LC0 = P2; +.Lword_loop_s: R0 = W[P0]; +.Lword_loop_e: W[P1++] = R0; + sti R3; + RTS; + +ENTRY(_insb) + P0 = R0; /* P0 = port */ + cli R3; + P1 = R1; /* P1 = address */ + P2 = R2; /* P2 = count */ + SSYNC; + LSETUP( .Lbyte_loop_s, .Lbyte_loop_e) LC0 = P2; +.Lbyte_loop_s: R0 = B[P0]; +.Lbyte_loop_e: B[P1++] = R0; + sti R3; + RTS; diff --git a/arch/blackfin/lib/lshrdi3.c b/arch/blackfin/lib/lshrdi3.c new file mode 100644 index 000000000000..84b9c5592220 --- /dev/null +++ b/arch/blackfin/lib/lshrdi3.c @@ -0,0 +1,72 @@ +/* + * File: arch/blackfin/lib/lshrdi3.c + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__ ((mode(SI))); +typedef unsigned int USItype __attribute__ ((mode(SI))); +typedef int DItype __attribute__ ((mode(DI))); +typedef int word_type __attribute__ ((mode(__word__))); + +struct DIstruct { + SItype high, low; +}; + +typedef union { + struct DIstruct s; + DItype ll; +} DIunion; + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +DItype __lshrdi3(DItype u, word_type b)__attribute__((l1_text)); +#endif + +DItype __lshrdi3(DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + w.s.high = 0; + w.s.low = (USItype) uu.s.high >> -bm; + } else { + USItype carries = (USItype) uu.s.high << bm; + w.s.high = (USItype) uu.s.high >> b; + w.s.low = ((USItype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/blackfin/lib/memchr.S b/arch/blackfin/lib/memchr.S new file mode 100644 index 000000000000..498122250d07 --- /dev/null +++ b/arch/blackfin/lib/memchr.S @@ -0,0 +1,70 @@ +/* + * File: arch/blackfin/lib/memchr.S + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +/* void *memchr(const void *s, int c, size_t n); + * R0 = address (s) + * R1 = sought byte (c) + * R2 = count (n) + * + * Returns pointer to located character. + */ + +.text + +.align 2 + +ENTRY(_memchr) + P0 = R0; /* P0 = address */ + P2 = R2; /* P2 = count */ + R1 = R1.B(Z); + CC = R2 == 0; + IF CC JUMP .Lfailed; + +.Lbytes: + LSETUP (.Lbyte_loop_s, .Lbyte_loop_e) LC0=P2; + +.Lbyte_loop_s: + R3 = B[P0++](Z); + CC = R3 == R1; + IF CC JUMP .Lfound; +.Lbyte_loop_e: + NOP; + +.Lfailed: + R0=0; + RTS; + +.Lfound: + R0 = P0; + R0 += -1; + RTS; + +.size _memchr,.-_memchr diff --git a/arch/blackfin/lib/memcmp.S b/arch/blackfin/lib/memcmp.S new file mode 100644 index 000000000000..5b9502368fc6 --- /dev/null +++ b/arch/blackfin/lib/memcmp.S @@ -0,0 +1,110 @@ +/* + * File: arch/blackfin/lib/memcmp.S + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +/* int memcmp(const void *s1, const void *s2, size_t n); + * R0 = First Address (s1) + * R1 = Second Address (s2) + * R2 = count (n) + * + * Favours word aligned data. + */ + +.text + +.align 2 + +ENTRY(_memcmp) + I1 = P3; + P0 = R0; /* P0 = s1 address */ + P3 = R1; /* P3 = s2 Address */ + P2 = R2 ; /* P2 = count */ + CC = R2 <= 7(IU); + IF CC JUMP .Ltoo_small; + I0 = R1; /* s2 */ + R1 = R1 | R0; /* OR addresses together */ + R1 <<= 30; /* check bottom two bits */ + CC = AZ; /* AZ set if zero. */ + IF !CC JUMP .Lbytes ; /* Jump if addrs not aligned. */ + + P1 = P2 >> 2; /* count = n/4 */ + R3 = 3; + R2 = R2 & R3; /* remainder */ + P2 = R2; /* set remainder */ + + LSETUP (.Lquad_loop_s, .Lquad_loop_e) LC0=P1; +.Lquad_loop_s: + MNOP || R0 = [P0++] || R1 = [I0++]; + CC = R0 == R1; + IF !CC JUMP .Lquad_different; +.Lquad_loop_e: + NOP; + + P3 = I0; /* s2 */ +.Ltoo_small: + CC = P2 == 0; /* Check zero count*/ + IF CC JUMP .Lfinished; /* very unlikely*/ + +.Lbytes: + LSETUP (.Lbyte_loop_s, .Lbyte_loop_e) LC0=P2; +.Lbyte_loop_s: + R1 = B[P3++](Z); /* *s2 */ + R0 = B[P0++](Z); /* *s1 */ + CC = R0 == R1; + IF !CC JUMP .Ldifferent; +.Lbyte_loop_e: + NOP; + +.Ldifferent: + R0 = R0 - R1; + P3 = I1; + RTS; + +.Lquad_different: + /* We've read two quads which don't match. + * Can't just compare them, because we're + * a little-endian machine, so the MSBs of + * the regs occur at later addresses in the + * string. + * Arrange to re-read those two quads again, + * byte-by-byte. + */ + P0 += -4; /* back up to the start of the */ + P3 = I0; /* quads, and increase the*/ + P2 += 4; /* remainder count*/ + P3 += -4; + JUMP .Lbytes; + +.Lfinished: + R0 = 0; + P3 = I1; + RTS; + +.size _memcmp,.-_memcmp diff --git a/arch/blackfin/lib/memcpy.S b/arch/blackfin/lib/memcpy.S new file mode 100644 index 000000000000..c1e00eff541c --- /dev/null +++ b/arch/blackfin/lib/memcpy.S @@ -0,0 +1,142 @@ +/* + * File: arch/blackfin/lib/memcpy.S + * Based on: + * Author: + * + * Created: + * Description: internal version of memcpy(), issued by the compiler + * to copy blocks of data around. + * This is really memmove() - it has to be able to deal with + * possible overlaps, because that ambiguity is when the compiler + * gives up and calls a function. We have our own, internal version + * so that we get something we trust, even if the user has redefined + * the normal symbol. + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +/* void *memcpy(void *dest, const void *src, size_t n); + * R0 = To Address (dest) (leave unchanged to form result) + * R1 = From Address (src) + * R2 = count + * + * Note: Favours word alignment + */ + +#ifdef CONFIG_MEMCPY_L1 +.section .l1.text +#else +.text +#endif + +.align 2 + +ENTRY(_memcpy) + CC = R2 <= 0; /* length not positive? */ + IF CC JUMP .L_P1L2147483647; /* Nothing to do */ + + P0 = R0 ; /* dst*/ + P1 = R1 ; /* src*/ + P2 = R2 ; /* length */ + + /* check for overlapping data */ + CC = R1 < R0; /* src < dst */ + IF !CC JUMP .Lno_overlap; + R3 = R1 + R2; + CC = R0 < R3; /* and dst < src+len */ + IF CC JUMP .Lhas_overlap; + +.Lno_overlap: + /* Check for aligned data.*/ + + R3 = R1 | R0; + R0 = 0x3; + R3 = R3 & R0; + CC = R3; /* low bits set on either address? */ + IF CC JUMP .Lnot_aligned; + + /* Both addresses are word-aligned, so we can copy + at least part of the data using word copies.*/ + P2 = P2 >> 2; + CC = P2 <= 2; + IF !CC JUMP .Lmore_than_seven; + /* less than eight bytes... */ + P2 = R2; + LSETUP(.Lthree_start, .Lthree_end) LC0=P2; + R0 = R1; /* setup src address for return */ +.Lthree_start: + R3 = B[P1++] (X); +.Lthree_end: + B[P0++] = R3; + + RTS; + +.Lmore_than_seven: + /* There's at least eight bytes to copy. */ + P2 += -1; /* because we unroll one iteration */ + LSETUP(.Lword_loop, .Lword_loop) LC0=P2; + R0 = R1; + I1 = P1; + R3 = [I1++]; +.Lword_loop: + MNOP || [P0++] = R3 || R3 = [I1++]; + + [P0++] = R3; + /* Any remaining bytes to copy? */ + R3 = 0x3; + R3 = R2 & R3; + CC = R3 == 0; + P1 = I1; /* in case there's something left, */ + IF !CC JUMP .Lbytes_left; + RTS; +.Lbytes_left: P2 = R3; +.Lnot_aligned: + /* From here, we're copying byte-by-byte. */ + LSETUP (.Lbyte_start, .Lbyte_end) LC0=P2; + R0 = R1; /* Save src address for return */ +.Lbyte_start: + R1 = B[P1++] (X); +.Lbyte_end: + B[P0++] = R1; + +.L_P1L2147483647: + RTS; + +.Lhas_overlap: + /* Need to reverse the copying, because the + * dst would clobber the src. + * Don't bother to work out alignment for + * the reverse case. + */ + R0 = R1; /* save src for later. */ + P0 = P0 + P2; + P0 += -1; + P1 = P1 + P2; + P1 += -1; + LSETUP(.Lover_start, .Lover_end) LC0=P2; +.Lover_start: + R1 = B[P1--] (X); +.Lover_end: + B[P0--] = R1; + + RTS; diff --git a/arch/blackfin/lib/memmove.S b/arch/blackfin/lib/memmove.S new file mode 100644 index 000000000000..2e5fb7f8df13 --- /dev/null +++ b/arch/blackfin/lib/memmove.S @@ -0,0 +1,103 @@ +/* + * File: arch/blackfin/lib/memmove.S + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +.align 2 + +/* + * C Library function MEMMOVE + * R0 = To Address (leave unchanged to form result) + * R1 = From Address + * R2 = count + * Data may overlap + */ + +ENTRY(_memmove) + I1 = P3; + P0 = R0; /* P0 = To address */ + P3 = R1; /* P3 = From Address */ + P2 = R2; /* P2 = count */ + CC = P2 == 0; /* Check zero count*/ + IF CC JUMP .Lfinished; /* very unlikely */ + + CC = R1 < R0 (IU); /* From < To */ + IF !CC JUMP .Lno_overlap; + R3 = R1 + R2; + CC = R0 <= R3 (IU); /* (From+len) >= To */ + IF CC JUMP .Loverlap; +.Lno_overlap: + R3 = 11; + CC = R2 <= R3; + IF CC JUMP .Lbytes; + R3 = R1 | R0; /* OR addresses together */ + R3 <<= 30; /* check bottom two bits */ + CC = AZ; /* AZ set if zero.*/ + IF !CC JUMP .Lbytes; /* Jump if addrs not aligned.*/ + + I0 = P3; + P1 = P2 >> 2; /* count = n/4 */ + P1 += -1; + R3 = 3; + R2 = R2 & R3; /* remainder */ + P2 = R2; /* set remainder */ + R1 = [I0++]; + + LSETUP (.Lquad_loop, .Lquad_loop) LC0=P1; +.Lquad_loop: MNOP || [P0++] = R1 || R1 = [I0++]; + [P0++] = R1; + + CC = P2 == 0; /* any remaining bytes? */ + P3 = I0; /* Ammend P3 to updated ptr. */ + IF !CC JUMP .Lbytes; + P3 = I1; + RTS; + +.Lbytes: LSETUP (.Lbyte2_s, .Lbyte2_e) LC0=P2; +.Lbyte2_s: R1 = B[P3++](Z); +.Lbyte2_e: B[P0++] = R1; + +.Lfinished: P3 = I1; + RTS; + +.Loverlap: + P2 += -1; + P0 = P0 + P2; + P3 = P3 + P2; + R1 = B[P3--] (Z); + CC = P2 == 0; + IF CC JUMP .Lno_loop; + LSETUP (.Lol_s, .Lol_e) LC0 = P2; +.Lol_s: B[P0--] = R1; +.Lol_e: R1 = B[P3--] (Z); +.Lno_loop: B[P0] = R1; + P3 = I1; + RTS; + +.size _memmove,.-_memmove diff --git a/arch/blackfin/lib/memset.S b/arch/blackfin/lib/memset.S new file mode 100644 index 000000000000..ba6d047568dd --- /dev/null +++ b/arch/blackfin/lib/memset.S @@ -0,0 +1,109 @@ +/* + * File: arch/blackfin/lib/memset.S + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +.align 2 + +#ifdef CONFIG_MEMSET_L1 +.section .l1.text +#else +.text +#endif + +/* + * C Library function MEMSET + * R0 = address (leave unchanged to form result) + * R1 = filler byte + * R2 = count + * Favours word aligned data. + */ + +ENTRY(_memset) + P0 = R0 ; /* P0 = address */ + P2 = R2 ; /* P2 = count */ + R3 = R0 + R2; /* end */ + CC = R2 <= 7(IU); + IF CC JUMP .Ltoo_small; + R1 = R1.B (Z); /* R1 = fill char */ + R2 = 3; + R2 = R0 & R2; /* addr bottom two bits */ + CC = R2 == 0; /* AZ set if zero. */ + IF !CC JUMP .Lforce_align ; /* Jump if addr not aligned. */ + +.Laligned: + P1 = P2 >> 2; /* count = n/4 */ + R2 = R1 << 8; /* create quad filler */ + R2.L = R2.L + R1.L(NS); + R2.H = R2.L + R1.H(NS); + P2 = R3; + + LSETUP (.Lquad_loop , .Lquad_loop) LC0=P1; +.Lquad_loop: + [P0++] = R2; + + CC = P0 == P2; + IF !CC JUMP .Lbytes_left; + RTS; + +.Lbytes_left: + R2 = R3; /* end point */ + R3 = P0; /* current position */ + R2 = R2 - R3; /* bytes left */ + P2 = R2; + +.Ltoo_small: + CC = P2 == 0; /* Check zero count */ + IF CC JUMP .Lfinished; /* Unusual */ + +.Lbytes: + LSETUP (.Lbyte_loop , .Lbyte_loop) LC0=P2; +.Lbyte_loop: + B[P0++] = R1; + +.Lfinished: + RTS; + +.Lforce_align: + CC = BITTST (R0, 0); /* odd byte */ + R0 = 4; + R0 = R0 - R2; + P1 = R0; + R0 = P0; /* Recover return address */ + IF !CC JUMP .Lskip1; + B[P0++] = R1; +.Lskip1: + CC = R2 <= 2; /* 2 bytes */ + P2 -= P1; /* reduce count */ + IF !CC JUMP .Laligned; + B[P0++] = R1; + B[P0++] = R1; + JUMP .Laligned; + +.size _memset,.-_memset diff --git a/arch/blackfin/lib/modsi3.S b/arch/blackfin/lib/modsi3.S new file mode 100644 index 000000000000..528b8b1ccb34 --- /dev/null +++ b/arch/blackfin/lib/modsi3.S @@ -0,0 +1,79 @@ +/* + * File: arch/blackfin/lib/modsi3.S + * Based on: + * Author: + * + * Created: + * Description: This program computes 32 bit signed remainder. It calls div32 function + * for quotient estimation. + * + * Registers used : + * Numerator/ Denominator in R0, R1 + * R0 - returns remainder. + * R2-R7 + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +.global ___modsi3; +.type ___modsi3, STT_FUNC; +.extern ___divsi3; +.type ___divsi3, STT_FUNC; + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +.section .l1.text +#else +.text +#endif + +___modsi3: + + CC=R0==0; + IF CC JUMP .LRETURN_R0; /* Return 0, if numerator == 0 */ + CC=R1==0; + IF CC JUMP .LRETURN_ZERO; /* Return 0, if denominator == 0 */ + CC=R0==R1; + IF CC JUMP .LRETURN_ZERO; /* Return 0, if numerator == denominator */ + CC = R1 == 1; + IF CC JUMP .LRETURN_ZERO; /* Return 0, if denominator == 1 */ + CC = R1 == -1; + IF CC JUMP .LRETURN_ZERO; /* Return 0, if denominator == -1 */ + + /* Valid input. Use __divsi3() to compute the quotient, and then + * derive the remainder from that. */ + + [--SP] = (R7:6); /* Push R7 and R6 */ + [--SP] = RETS; /* and return address */ + R7 = R0; /* Copy of R0 */ + R6 = R1; /* Save for later */ + SP += -12; /* Should always provide this space */ + CALL ___divsi3; /* Compute signed quotient using ___divsi3()*/ + SP += 12; + R0 *= R6; /* Quotient * divisor */ + R0 = R7 - R0; /* Dividend - (quotient * divisor) */ + RETS = [SP++]; /* Get back return address */ + (R7:6) = [SP++]; /* Pop registers R7 and R4 */ + RTS; /* Store remainder */ + +.LRETURN_ZERO: + R0 = 0; +.LRETURN_R0: + RTS; diff --git a/arch/blackfin/lib/muldi3.c b/arch/blackfin/lib/muldi3.c new file mode 100644 index 000000000000..303d0c6a6dba --- /dev/null +++ b/arch/blackfin/lib/muldi3.c @@ -0,0 +1,99 @@ +/* + * File: arch/blackfin/lib/muldi3.c + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SI_TYPE_SIZE +#define SI_TYPE_SIZE 32 +#endif +#define __ll_b (1L << (SI_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((usitype) (t) % __ll_b) +#define __ll_highpart(t) ((usitype) (t) / __ll_b) +#define BITS_PER_UNIT 8 + +#if !defined(umul_ppmm) +#define umul_ppmm(w1, w0, u, v) \ + do { \ + usitype __x0, __x1, __x2, __x3; \ + usitype __ul, __vl, __uh, __vh; \ + \ + __ul = __ll_lowpart (u); \ + __uh = __ll_highpart (u); \ + __vl = __ll_lowpart (v); \ + __vh = __ll_highpart (v); \ + \ + __x0 = (usitype) __ul * __vl; \ + __x1 = (usitype) __ul * __vh; \ + __x2 = (usitype) __uh * __vl; \ + __x3 = (usitype) __uh * __vh; \ + \ + __x1 += __ll_highpart (__x0);/* this can't give carry */ \ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += __ll_b; /* yes, add it in the proper pos. */ \ + \ + (w1) = __x3 + __ll_highpart (__x1); \ + (w0) = __ll_lowpart (__x1) * __ll_b + __ll_lowpart (__x0); \ + } while (0) +#endif + +#if !defined(__umulsidi3) +#define __umulsidi3(u, v) \ + ({diunion __w; \ + umul_ppmm (__w.s.high, __w.s.low, u, v); \ + __w.ll; }) +#endif + +typedef unsigned int usitype __attribute__ ((mode(SI))); +typedef int sitype __attribute__ ((mode(SI))); +typedef int ditype __attribute__ ((mode(DI))); +typedef int word_type __attribute__ ((mode(__word__))); + +struct distruct { + sitype low, high; +}; +typedef union { + struct distruct s; + ditype ll; +} diunion; + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +ditype __muldi3(ditype u, ditype v)__attribute__((l1_text)); +#endif + +ditype __muldi3(ditype u, ditype v) +{ + diunion w; + diunion uu, vv; + + uu.ll = u, vv.ll = v; + w.ll = __umulsidi3(uu.s.low, vv.s.low); + w.s.high += ((usitype) uu.s.low * (usitype) vv.s.high + + (usitype) uu.s.high * (usitype) vv.s.low); + + return w.ll; +} diff --git a/arch/blackfin/lib/outs.S b/arch/blackfin/lib/outs.S new file mode 100644 index 000000000000..f8c876fe8930 --- /dev/null +++ b/arch/blackfin/lib/outs.S @@ -0,0 +1,62 @@ +/* + * File: arch/blackfin/lib/outs.S + * Based on: + * Author: Bas Vermeulen <bas@buyways.nl> + * + * Created: Tue Mar 22 15:27:24 CEST 2005 + * Description: Implementation of outs{bwl} for BlackFin processors using zero overhead loops. + * + * Modified: Copyright (C) 2005 Bas Vermeulen, BuyWays BV <bas@buyways.nl> + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +.align 2 + +ENTRY(_outsl) + P0 = R0; /* P0 = port */ + P1 = R1; /* P1 = address */ + P2 = R2; /* P2 = count */ + + LSETUP( .Llong_loop_s, .Llong_loop_e) LC0 = P2; +.Llong_loop_s: R0 = [P1++]; +.Llong_loop_e: [P0] = R0; + RTS; + +ENTRY(_outsw) + P0 = R0; /* P0 = port */ + P1 = R1; /* P1 = address */ + P2 = R2; /* P2 = count */ + + LSETUP( .Lword_loop_s, .Lword_loop_e) LC0 = P2; +.Lword_loop_s: R0 = W[P1++]; +.Lword_loop_e: W[P0] = R0; + RTS; + +ENTRY(_outsb) + P0 = R0; /* P0 = port */ + P1 = R1; /* P1 = address */ + P2 = R2; /* P2 = count */ + + LSETUP( .Lbyte_loop_s, .Lbyte_loop_e) LC0 = P2; +.Lbyte_loop_s: R0 = B[P1++]; +.Lbyte_loop_e: B[P0] = R0; + RTS; diff --git a/arch/blackfin/lib/smulsi3_highpart.S b/arch/blackfin/lib/smulsi3_highpart.S new file mode 100644 index 000000000000..10b8f8da576f --- /dev/null +++ b/arch/blackfin/lib/smulsi3_highpart.S @@ -0,0 +1,30 @@ +.align 2 +.global ___smulsi3_highpart; +.type ___smulsi3_highpart, STT_FUNC; + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +.section .l1.text +#else +.text +#endif + +___smulsi3_highpart: + R2 = R1.L * R0.L (FU); + R3 = R1.H * R0.L (IS,M); + R0 = R0.H * R1.H, R1 = R0.H * R1.L (IS,M); + + R1.L = R2.H + R1.L; + cc = ac0; + R2 = cc; + + R1.L = R1.L + R3.L; + cc = ac0; + R1 >>>= 16; + R3 >>>= 16; + R1 = R1 + R3; + R1 = R1 + R2; + R2 = cc; + R1 = R1 + R2; + + R0 = R0 + R1; + RTS; diff --git a/arch/blackfin/lib/strcmp.c b/arch/blackfin/lib/strcmp.c new file mode 100644 index 000000000000..2ad47c4254ba --- /dev/null +++ b/arch/blackfin/lib/strcmp.c @@ -0,0 +1,11 @@ +#include <linux/types.h> + +#define strcmp __inline_strcmp +#include <asm/string.h> +#undef strcmp + +int strcmp(const char *dest, const char *src) +{ + return __inline_strcmp(dest, src); +} + diff --git a/arch/blackfin/lib/strcpy.c b/arch/blackfin/lib/strcpy.c new file mode 100644 index 000000000000..4dc835a8a19b --- /dev/null +++ b/arch/blackfin/lib/strcpy.c @@ -0,0 +1,11 @@ +#include <linux/types.h> + +#define strcpy __inline_strcpy +#include <asm/string.h> +#undef strcpy + +char *strcpy(char *dest, const char *src) +{ + return __inline_strcpy(dest, src); +} + diff --git a/arch/blackfin/lib/strncmp.c b/arch/blackfin/lib/strncmp.c new file mode 100644 index 000000000000..947bcfe3f3bb --- /dev/null +++ b/arch/blackfin/lib/strncmp.c @@ -0,0 +1,11 @@ +#include <linux/types.h> + +#define strncmp __inline_strncmp +#include <asm/string.h> +#undef strncmp + +int strncmp(const char *cs, const char *ct, size_t count) +{ + return __inline_strncmp(cs, ct, count); +} + diff --git a/arch/blackfin/lib/strncpy.c b/arch/blackfin/lib/strncpy.c new file mode 100644 index 000000000000..77a9b2e95097 --- /dev/null +++ b/arch/blackfin/lib/strncpy.c @@ -0,0 +1,11 @@ +#include <linux/types.h> + +#define strncpy __inline_strncpy +#include <asm/string.h> +#undef strncpy + +char *strncpy(char *dest, const char *src, size_t n) +{ + return __inline_strncpy(dest, src, n); +} + diff --git a/arch/blackfin/lib/udivsi3.S b/arch/blackfin/lib/udivsi3.S new file mode 100644 index 000000000000..d39a12916259 --- /dev/null +++ b/arch/blackfin/lib/udivsi3.S @@ -0,0 +1,298 @@ +/* + * File: arch/blackfin/lib/udivsi3.S + * Based on: + * Author: + * + * Created: + * Description: + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/linkage.h> + +#define CARRY AC0 + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +.section .l1.text +#else +.text +#endif + + +ENTRY(___udivsi3) + + CC = R0 < R1 (IU); /* If X < Y, always return 0 */ + IF CC JUMP .Lreturn_ident; + + R2 = R1 << 16; + CC = R2 <= R0 (IU); + IF CC JUMP .Lidents; + + R2 = R0 >> 31; /* if X is a 31-bit number */ + R3 = R1 >> 15; /* and Y is a 15-bit number */ + R2 = R2 | R3; /* then it's okay to use the DIVQ builtins (fallthrough to fast)*/ + CC = R2; + IF CC JUMP .Ly_16bit; + +/* METHOD 1: FAST DIVQ + We know we have a 31-bit dividend, and 15-bit divisor so we can use the + simple divq approach (first setting AQ to 0 - implying unsigned division, + then 16 DIVQ's). +*/ + + AQ = CC; /* Clear AQ (CC==0) */ + +/* ISR States: When dividing two integers (32.0/16.0) using divide primitives, + we need to shift the dividend one bit to the left. + We have already checked that we have a 31-bit number so we are safe to do + that. +*/ + R0 <<= 1; + DIVQ(R0, R1); // 1 + DIVQ(R0, R1); // 2 + DIVQ(R0, R1); // 3 + DIVQ(R0, R1); // 4 + DIVQ(R0, R1); // 5 + DIVQ(R0, R1); // 6 + DIVQ(R0, R1); // 7 + DIVQ(R0, R1); // 8 + DIVQ(R0, R1); // 9 + DIVQ(R0, R1); // 10 + DIVQ(R0, R1); // 11 + DIVQ(R0, R1); // 12 + DIVQ(R0, R1); // 13 + DIVQ(R0, R1); // 14 + DIVQ(R0, R1); // 15 + DIVQ(R0, R1); // 16 + R0 = R0.L (Z); + RTS; + +.Ly_16bit: + /* We know that the upper 17 bits of Y might have bits set, + ** or that the sign bit of X might have a bit. If Y is a + ** 16-bit number, but not bigger, then we can use the builtins + ** with a post-divide correction. + ** R3 currently holds Y>>15, which means R3's LSB is the + ** bit we're interested in. + */ + + /* According to the ISR, to use the Divide primitives for + ** unsigned integer divide, the useable range is 31 bits + */ + CC = ! BITTST(R0, 31); + + /* IF condition is true we can scale our inputs and use the divide primitives, + ** with some post-adjustment + */ + R3 += -1; /* if so, Y is 0x00008nnn */ + CC &= AZ; + + /* If condition is true we can scale our inputs and use the divide primitives, + ** with some post-adjustment + */ + R3 = R1 >> 1; /* Pre-scaled divisor for primitive case */ + R2 = R0 >> 16; + + R2 = R3 - R2; /* shifted divisor < upper 16 bits of dividend */ + CC &= CARRY; + IF CC JUMP .Lshift_and_correct; + + /* Fall through to the identities */ + +/* METHOD 2: identities and manual calculation + We are not able to use the divide primites, but may still catch some special + cases. +*/ +.Lidents: + /* Test for common identities. Value to be returned is placed in R2. */ + CC = R0 == 0; /* 0/Y => 0 */ + IF CC JUMP .Lreturn_r0; + CC = R0 == R1; /* X==Y => 1 */ + IF CC JUMP .Lreturn_ident; + CC = R1 == 1; /* X/1 => X */ + IF CC JUMP .Lreturn_ident; + + R2.L = ONES R1; + R2 = R2.L (Z); + CC = R2 == 1; + IF CC JUMP .Lpower_of_two; + + [--SP] = (R7:5); /* Push registers R5-R7 */ + + /* Idents don't match. Go for the full operation. */ + + + R6 = 2; /* assume we'll shift two */ + R3 = 1; + + P2 = R1; + /* If either R0 or R1 have sign set, */ + /* divide them by two, and note it's */ + /* been done. */ + CC = R1 < 0; + R2 = R1 >> 1; + IF CC R1 = R2; /* Possibly-shifted R1 */ + IF !CC R6 = R3; /* R1 doesn't, so at most 1 shifted */ + + P0 = 0; + R3 = -R1; + [--SP] = R3; + R2 = R0 >> 1; + R2 = R0 >> 1; + CC = R0 < 0; + IF CC P0 = R6; /* Number of values divided */ + IF !CC R2 = R0; /* Shifted R0 */ + + /* P0 is 0, 1 (NR/=2) or 2 (NR/=2, DR/=2) */ + + /* r2 holds Copy dividend */ + R3 = 0; /* Clear partial remainder */ + R7 = 0; /* Initialise quotient bit */ + + P1 = 32; /* Set loop counter */ + LSETUP(.Lulst, .Lulend) LC0 = P1; /* Set loop counter */ +.Lulst: R6 = R2 >> 31; /* R6 = sign bit of R2, for carry */ + R2 = R2 << 1; /* Shift 64 bit dividend up by 1 bit */ + R3 = R3 << 1 || R5 = [SP]; + R3 = R3 | R6; /* Include any carry */ + CC = R7 < 0; /* Check quotient(AQ) */ + /* If AQ==0, we'll sub divisor */ + IF CC R5 = R1; /* and if AQ==1, we'll add it. */ + R3 = R3 + R5; /* Add/sub divsor to partial remainder */ + R7 = R3 ^ R1; /* Generate next quotient bit */ + + R5 = R7 >> 31; /* Get AQ */ + BITTGL(R5, 0); /* Invert it, to get what we'll shift */ +.Lulend: R2 = R2 + R5; /* and "shift" it in. */ + + CC = P0 == 0; /* Check how many inputs we shifted */ + IF CC JUMP .Lno_mult; /* if none... */ + R6 = R2 << 1; + CC = P0 == 1; + IF CC R2 = R6; /* if 1, Q = Q*2 */ + IF !CC R1 = P2; /* if 2, restore stored divisor */ + + R3 = R2; /* Copy of R2 */ + R3 *= R1; /* Q * divisor */ + R5 = R0 - R3; /* Z = (dividend - Q * divisor) */ + CC = R1 <= R5 (IU); /* Check if divisor <= Z? */ + R6 = CC; /* if yes, R6 = 1 */ + R2 = R2 + R6; /* if yes, add one to quotient(Q) */ +.Lno_mult: + SP += 4; + (R7:5) = [SP++]; /* Pop registers R5-R7 */ + R0 = R2; /* Store quotient */ + RTS; + +.Lreturn_ident: + CC = R0 < R1 (IU); /* If X < Y, always return 0 */ + R2 = 0; + IF CC JUMP .Ltrue_return_ident; + R2 = -1 (X); /* X/0 => 0xFFFFFFFF */ + CC = R1 == 0; + IF CC JUMP .Ltrue_return_ident; + R2 = -R2; /* R2 now 1 */ + CC = R0 == R1; /* X==Y => 1 */ + IF CC JUMP .Ltrue_return_ident; + R2 = R0; /* X/1 => X */ + /*FALLTHRU*/ + +.Ltrue_return_ident: + R0 = R2; +.Lreturn_r0: + RTS; + +.Lpower_of_two: + /* Y has a single bit set, which means it's a power of two. + ** That means we can perform the division just by shifting + ** X to the right the appropriate number of bits + */ + + /* signbits returns the number of sign bits, minus one. + ** 1=>30, 2=>29, ..., 0x40000000=>0. Which means we need + ** to shift right n-signbits spaces. It also means 0x80000000 + ** is a special case, because that *also* gives a signbits of 0 + */ + + R2 = R0 >> 31; + CC = R1 < 0; + IF CC JUMP .Ltrue_return_ident; + + R1.l = SIGNBITS R1; + R1 = R1.L (Z); + R1 += -30; + R0 = LSHIFT R0 by R1.L; + RTS; + +/* METHOD 3: PRESCALE AND USE THE DIVIDE PRIMITIVES WITH SOME POST-CORRECTION + Two scaling operations are required to use the divide primitives with a + divisor > 0x7FFFF. + Firstly (as in method 1) we need to shift the dividend 1 to the left for + integer division. + Secondly we need to shift both the divisor and dividend 1 to the right so + both are in range for the primitives. + The left/right shift of the dividend does nothing so we can skip it. +*/ +.Lshift_and_correct: + R2 = R0; + // R3 is already R1 >> 1 + CC=!CC; + AQ = CC; /* Clear AQ, got here with CC = 0 */ + DIVQ(R2, R3); // 1 + DIVQ(R2, R3); // 2 + DIVQ(R2, R3); // 3 + DIVQ(R2, R3); // 4 + DIVQ(R2, R3); // 5 + DIVQ(R2, R3); // 6 + DIVQ(R2, R3); // 7 + DIVQ(R2, R3); // 8 + DIVQ(R2, R3); // 9 + DIVQ(R2, R3); // 10 + DIVQ(R2, R3); // 11 + DIVQ(R2, R3); // 12 + DIVQ(R2, R3); // 13 + DIVQ(R2, R3); // 14 + DIVQ(R2, R3); // 15 + DIVQ(R2, R3); // 16 + + /* According to the Instruction Set Reference: + To divide by a divisor > 0x7FFF, + 1. prescale and perform divide to obtain quotient (Q) (done above), + 2. multiply quotient by unscaled divisor (result M) + 3. subtract the product from the divident to get an error (E = X - M) + 4. if E < divisor (Y) subtract 1, if E > divisor (Y) add 1, else return quotient (Q) + */ + R3 = R2.L (Z); /* Q = X' / Y' */ + R2 = R3; /* Preserve Q */ + R2 *= R1; /* M = Q * Y */ + R2 = R0 - R2; /* E = X - M */ + R0 = R3; /* Copy Q into result reg */ + +/* Correction: If result of the multiply is negative, we overflowed + and need to correct the result by subtracting 1 from the result.*/ + R3 = 0xFFFF (Z); + R2 = R2 >> 16; /* E >> 16 */ + CC = R2 == R3; + R3 = 1 ; + R1 = R0 - R3; + IF CC R0 = R1; + RTS; diff --git a/arch/blackfin/lib/umodsi3.S b/arch/blackfin/lib/umodsi3.S new file mode 100644 index 000000000000..b55ce96ab89f --- /dev/null +++ b/arch/blackfin/lib/umodsi3.S @@ -0,0 +1,66 @@ +/* + * File: arch/blackfin/lib/umodsi3.S + * Based on: + * Author: + * + * Created: + * Description: libgcc1 routines for Blackfin 5xx + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +.section .l1.text +#else +.text +#endif + +.extern ___udivsi3; +.globl ___umodsi3 +___umodsi3: + + CC=R0==0; + IF CC JUMP .LRETURN_R0; /* Return 0, if NR == 0 */ + CC= R1==0; + IF CC JUMP .LRETURN_ZERO_VAL; /* Return 0, if DR == 0 */ + CC=R0==R1; + IF CC JUMP .LRETURN_ZERO_VAL; /* Return 0, if NR == DR */ + CC = R1 == 1; + IF CC JUMP .LRETURN_ZERO_VAL; /* Return 0, if DR == 1 */ + CC = R0<R1 (IU); + IF CC JUMP .LRETURN_R0; /* Return dividend (R0),IF NR<DR */ + + [--SP] = (R7:6); /* Push registers and */ + [--SP] = RETS; /* Return address */ + R7 = R0; /* Copy of R0 */ + R6 = R1; + SP += -12; /* Should always provide this space */ + CALL ___udivsi3; /* Compute unsigned quotient using ___udiv32()*/ + SP += 12; + R0 *= R6; /* Quotient * divisor */ + R0 = R7 - R0; /* Dividend - (quotient * divisor) */ + RETS = [SP++]; /* Pop return address */ + ( R7:6) = [SP++]; /* And registers */ + RTS; /* Return remainder */ +.LRETURN_ZERO_VAL: + R0 = 0; +.LRETURN_R0: + RTS; diff --git a/arch/blackfin/lib/umulsi3_highpart.S b/arch/blackfin/lib/umulsi3_highpart.S new file mode 100644 index 000000000000..aac8218fb258 --- /dev/null +++ b/arch/blackfin/lib/umulsi3_highpart.S @@ -0,0 +1,23 @@ +.align 2 +.global ___umulsi3_highpart; +.type ___umulsi3_highpart, STT_FUNC; + +#ifdef CONFIG_ARITHMETIC_OPS_L1 +.section .l1.text +#else +.text +#endif + +___umulsi3_highpart: + R2 = R1.H * R0.H, R3 = R1.L * R0.H (FU); + R0 = R1.L * R0.L, R1 = R1.H * R0.L (FU); + R0 >>= 16; + /* Unsigned multiplication has the nice property that we can + ignore carry on this first addition. */ + R0 = R0 + R3; + R0 = R0 + R1; + cc = ac0; + R1 = cc; + R1 = PACK(R1.l,R0.h); + R0 = R1 + R2; + RTS; |