Commit 9a8fd558 authored by Chris Zankel's avatar Chris Zankel Committed by Linus Torvalds
Browse files

[PATCH] xtensa: Architecture support for Tensilica Xtensa Part 6



The attached patches provides part 6 of an architecture implementation for the
Tensilica Xtensa CPU series.

Signed-off-by: default avatarChris Zankel <chris@zankel.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3f65ce4d
/*
* include/asm-xtensa/addrspace.h
*
* Dummy a.out file. Xtensa does not support the a.out format, but the kernel
* seems to depend on it.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*/
#ifndef _XTENSA_A_OUT_H
#define _XTENSA_A_OUT_H
/* Note: the kernel needs the a.out definitions, even if only ELF is used. */
#define STACK_TOP TASK_SIZE
struct exec
{
unsigned long a_info;
unsigned a_text;
unsigned a_data;
unsigned a_bss;
unsigned a_syms;
unsigned a_entry;
unsigned a_trsize;
unsigned a_drsize;
};
#endif /* _XTENSA_A_OUT_H */
/*
* include/asm-xtensa/atomic.h
*
* Atomic operations that C can't guarantee us. Useful for resource counting..
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*/
#ifndef _XTENSA_ATOMIC_H
#define _XTENSA_ATOMIC_H
#include <linux/config.h>
#include <linux/stringify.h>
typedef struct { volatile int counter; } atomic_t;
#ifdef __KERNEL__
#include <asm/processor.h>
#include <asm/system.h>
#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
/*
* This Xtensa implementation assumes that the right mechanism
* for exclusion is for locking interrupts to level 1.
*
* Locking interrupts looks like this:
*
* rsil a15, 1
* <code>
* wsr a15, PS
* rsync
*
* Note that a15 is used here because the register allocation
* done by the compiler is not guaranteed and a window overflow
* may not occur between the rsil and wsr instructions. By using
* a15 in the rsil, the machine is guaranteed to be in a state
* where no register reference will cause an overflow.
*/
/**
* atomic_read - read atomic variable
* @v: pointer of type atomic_t
*
* Atomically reads the value of @v.
*/
#define atomic_read(v) ((v)->counter)
/**
* atomic_set - set atomic variable
* @v: pointer of type atomic_t
* @i: required value
*
* Atomically sets the value of @v to @i.
*/
#define atomic_set(v,i) ((v)->counter = (i))
/**
* atomic_add - add integer to atomic variable
* @i: integer value to add
* @v: pointer of type atomic_t
*
* Atomically adds @i to @v.
*/
extern __inline__ void atomic_add(int i, atomic_t * v)
{
unsigned int vval;
__asm__ __volatile__(
"rsil a15, "__stringify(LOCKLEVEL)"\n\t"
"l32i %0, %2, 0 \n\t"
"add %0, %0, %1 \n\t"
"s32i %0, %2, 0 \n\t"
"wsr a15, "__stringify(PS)" \n\t"
"rsync \n"
: "=&a" (vval)
: "a" (i), "a" (v)
: "a15", "memory"
);
}
/**
* atomic_sub - subtract the atomic variable
* @i: integer value to subtract
* @v: pointer of type atomic_t
*
* Atomically subtracts @i from @v.
*/
extern __inline__ void atomic_sub(int i, atomic_t *v)
{
unsigned int vval;
__asm__ __volatile__(
"rsil a15, "__stringify(LOCKLEVEL)"\n\t"
"l32i %0, %2, 0 \n\t"
"sub %0, %0, %1 \n\t"
"s32i %0, %2, 0 \n\t"
"wsr a15, "__stringify(PS)" \n\t"
"rsync \n"
: "=&a" (vval)
: "a" (i), "a" (v)
: "a15", "memory"
);
}
/*
* We use atomic_{add|sub}_return to define other functions.
*/
extern __inline__ int atomic_add_return(int i, atomic_t * v)
{
unsigned int vval;
__asm__ __volatile__(
"rsil a15,"__stringify(LOCKLEVEL)"\n\t"
"l32i %0, %2, 0 \n\t"
"add %0, %0, %1 \n\t"
"s32i %0, %2, 0 \n\t"
"wsr a15, "__stringify(PS)" \n\t"
"rsync \n"
: "=&a" (vval)
: "a" (i), "a" (v)
: "a15", "memory"
);
return vval;
}
extern __inline__ int atomic_sub_return(int i, atomic_t * v)
{
unsigned int vval;
__asm__ __volatile__(
"rsil a15,"__stringify(LOCKLEVEL)"\n\t"
"l32i %0, %2, 0 \n\t"
"sub %0, %0, %1 \n\t"
"s32i %0, %2, 0 \n\t"
"wsr a15, "__stringify(PS)" \n\t"
"rsync \n"
: "=&a" (vval)
: "a" (i), "a" (v)
: "a15", "memory"
);
return vval;
}
/**
* atomic_sub_and_test - subtract value from variable and test result
* @i: integer value to subtract
* @v: pointer of type atomic_t
*
* Atomically subtracts @i from @v and returns
* true if the result is zero, or false for all
* other cases.
*/
#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0)
/**
* atomic_inc - increment atomic variable
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1.
*/
#define atomic_inc(v) atomic_add(1,(v))
/**
* atomic_inc - increment atomic variable
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1.
*/
#define atomic_inc_return(v) atomic_add_return(1,(v))
/**
* atomic_dec - decrement atomic variable
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1.
*/
#define atomic_dec(v) atomic_sub(1,(v))
/**
* atomic_dec_return - decrement atomic variable
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1.
*/
#define atomic_dec_return(v) atomic_sub_return(1,(v))
/**
* atomic_dec_and_test - decrement and test
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1 and
* returns true if the result is 0, or false for all other
* cases.
*/
#define atomic_dec_and_test(v) (atomic_sub_return(1,(v)) == 0)
/**
* atomic_inc_and_test - increment and test
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases.
*/
#define atomic_inc_and_test(v) (atomic_add_return(1,(v)) == 0)
/**
* atomic_add_negative - add and test if negative
* @v: pointer of type atomic_t
* @i: integer value to add
*
* Atomically adds @i to @v and returns true
* if the result is negative, or false when
* result is greater than or equal to zero.
*/
#define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0)
extern __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v)
{
unsigned int all_f = -1;
unsigned int vval;
__asm__ __volatile__(
"rsil a15,"__stringify(LOCKLEVEL)"\n\t"
"l32i %0, %2, 0 \n\t"
"xor %1, %4, %3 \n\t"
"and %0, %0, %4 \n\t"
"s32i %0, %2, 0 \n\t"
"wsr a15, "__stringify(PS)" \n\t"
"rsync \n"
: "=&a" (vval), "=a" (mask)
: "a" (v), "a" (all_f), "1" (mask)
: "a15", "memory"
);
}
extern __inline__ void atomic_set_mask(unsigned int mask, atomic_t *v)
{
unsigned int vval;
__asm__ __volatile__(
"rsil a15,"__stringify(LOCKLEVEL)"\n\t"
"l32i %0, %2, 0 \n\t"
"or %0, %0, %1 \n\t"
"s32i %0, %2, 0 \n\t"
"wsr a15, "__stringify(PS)" \n\t"
"rsync \n"
: "=&a" (vval)
: "a" (mask), "a" (v)
: "a15", "memory"
);
}
/* Atomic operations are already serializing */
#define smp_mb__before_atomic_dec() barrier()
#define smp_mb__after_atomic_dec() barrier()
#define smp_mb__before_atomic_inc() barrier()
#define smp_mb__after_atomic_inc() barrier()
#endif /* __KERNEL__ */
#endif /* _XTENSA_ATOMIC_H */
/*
* include/asm-xtensa/bitops.h
*
* Atomic operations that C can't guarantee us.Useful for resource counting etc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*/
#ifndef _XTENSA_BITOPS_H
#define _XTENSA_BITOPS_H
#ifdef __KERNEL__
#include <asm/processor.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#ifdef CONFIG_SMP
# error SMP not supported on this architecture
#endif
static __inline__ void set_bit(int nr, volatile void * addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long flags;
local_irq_save(flags);
*a |= mask;
local_irq_restore(flags);
}
static __inline__ void __set_bit(int nr, volatile unsigned long * addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
*a |= mask;
}
static __inline__ void clear_bit(int nr, volatile void * addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long flags;
local_irq_save(flags);
*a &= ~mask;
local_irq_restore(flags);
}
static __inline__ void __clear_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
*a &= ~mask;
}
/*
* clear_bit() doesn't provide any barrier for the compiler.
*/
#define smp_mb__before_clear_bit() barrier()
#define smp_mb__after_clear_bit() barrier()
static __inline__ void change_bit(int nr, volatile void * addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long flags;
local_irq_save(flags);
*a ^= mask;
local_irq_restore(flags);
}
static __inline__ void __change_bit(int nr, volatile void * addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
*a ^= mask;
}
static __inline__ int test_and_set_bit(int nr, volatile void * addr)
{
unsigned long retval;
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long flags;
local_irq_save(flags);
retval = (mask & *a) != 0;
*a |= mask;
local_irq_restore(flags);
return retval;
}
static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
{
unsigned long retval;
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
retval = (mask & *a) != 0;
*a |= mask;
return retval;
}
static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
{
unsigned long retval;
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long flags;
local_irq_save(flags);
retval = (mask & *a) != 0;
*a &= ~mask;
local_irq_restore(flags);
return retval;
}
static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long old = *a;
*a = old & ~mask;
return (old & mask) != 0;
}
static __inline__ int test_and_change_bit(int nr, volatile void * addr)
{
unsigned long retval;
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long flags;
local_irq_save(flags);
retval = (mask & *a) != 0;
*a ^= mask;
local_irq_restore(flags);
return retval;
}
/*
* non-atomic version; can be reordered
*/
static __inline__ int __test_and_change_bit(int nr, volatile void *addr)
{
unsigned long mask = 1 << (nr & 0x1f);
unsigned long *a = ((unsigned long *)addr) + (nr >> 5);
unsigned long old = *a;
*a = old ^ mask;
return (old & mask) != 0;
}
static __inline__ int test_bit(int nr, const volatile void *addr)
{
return 1UL & (((const volatile unsigned int *)addr)[nr>>5] >> (nr&31));
}
#if XCHAL_HAVE_NSAU
static __inline__ int __cntlz (unsigned long x)
{
int lz;
asm ("nsau %0, %1" : "=r" (lz) : "r" (x));
return 31 - lz;
}
#else
static __inline__ int __cntlz (unsigned long x)
{
unsigned long sum, x1, x2, x4, x8, x16;
x1 = x & 0xAAAAAAAA;
x2 = x & 0xCCCCCCCC;
x4 = x & 0xF0F0F0F0;
x8 = x & 0xFF00FF00;
x16 = x & 0xFFFF0000;
sum = x2 ? 2 : 0;
sum += (x16 != 0) * 16;
sum += (x8 != 0) * 8;
sum += (x4 != 0) * 4;
sum += (x1 != 0);
return sum;
}
#endif
/*
* ffz: Find first zero in word. Undefined if no zero exists.
* bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
*/
static __inline__ int ffz(unsigned long x)
{
if ((x = ~x) == 0)
return 32;
return __cntlz(x & -x);
}
/*
* __ffs: Find first bit set in word. Return 0 for bit 0
*/
static __inline__ int __ffs(unsigned long x)
{
return __cntlz(x & -x);
}
/*
* ffs: Find first bit set in word. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
* differs in spirit from the above ffz (man ffs).
*/
static __inline__ int ffs(unsigned long x)
{
return __cntlz(x & -x) + 1;
}
/*
* fls: Find last (most-significant) bit set in word.
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
*/
static __inline__ int fls (unsigned int x)
{
return __cntlz(x);
}
static __inline__ int
find_next_bit(const unsigned long *addr, int size, int offset)
{
const unsigned long *p = addr + (offset >> 5);
unsigned long result = offset & ~31UL;
unsigned long tmp;
if (offset >= size)
return size;
size -= result;
offset &= 31UL;
if (offset) {
tmp = *p++;
tmp &= ~0UL << offset;
if (size < 32)
goto found_first;
if (tmp)
goto found_middle;
size -= 32;
result += 32;
}
while (size >= 32) {
if ((tmp = *p++) != 0)
goto found_middle;
result += 32;
size -= 32;
}
if (!size)
return result;
tmp = *p;
found_first:
tmp &= ~0UL >> (32 - size);
if (tmp == 0UL) /* Are any bits set? */
return result + size; /* Nope. */
found_middle:
return result + __ffs(tmp);
}
/**
* find_first_bit - find the first set bit in a memory region
* @addr: The address to start the search at
* @size: The maximum size to search
*
* Returns the bit-number of the first set bit, not the number of the byte
* containing a bit.
*/
#define find_first_bit(addr, size) \
find_next_bit((addr), (size), 0)
static __inline__ int
find_next_zero_bit(const unsigned long *addr, int size, int offset)
{
const unsigned long *p = addr + (offset >> 5);
unsigned long result = offset & ~31UL;
unsigned long tmp;
if (offset >= size)
return size;
size -= result;
offset &= 31UL;
if (offset) {
tmp = *p++;
tmp |= ~0UL >> (32-offset);
if (size < 32)
<