aboutsummaryrefslogtreecommitdiff
path: root/arch/blackfin/mm
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@csr.com>2008-10-20 16:07:19 +0100
committerDavid Vrabel <david.vrabel@csr.com>2008-10-20 16:07:19 +0100
commit61e0e79ee3c609eb34edf2fe023708cba6a79b1f (patch)
tree663deacffd4071120dc9badb70428fe5f124c7b9 /arch/blackfin/mm
parentc15895ef30c2c03e99802951787183039a349d32 (diff)
parent0cfd81031a26717fe14380d18275f8e217571615 (diff)
Merge branch 'master' into for-upstream
Conflicts: Documentation/ABI/testing/sysfs-bus-usb drivers/Makefile
Diffstat (limited to 'arch/blackfin/mm')
-rw-r--r--arch/blackfin/mm/Makefile2
-rw-r--r--arch/blackfin/mm/isram-driver.c201
-rw-r--r--arch/blackfin/mm/sram-alloc.c (renamed from arch/blackfin/mm/blackfin_sram.c)31
3 files changed, 219 insertions, 15 deletions
diff --git a/arch/blackfin/mm/Makefile b/arch/blackfin/mm/Makefile
index 2a7202ce01f..d489f894f4b 100644
--- a/arch/blackfin/mm/Makefile
+++ b/arch/blackfin/mm/Makefile
@@ -2,4 +2,4 @@
# arch/blackfin/mm/Makefile
#
-obj-y := blackfin_sram.o init.o
+obj-y := sram-alloc.o isram-driver.o init.o
diff --git a/arch/blackfin/mm/isram-driver.c b/arch/blackfin/mm/isram-driver.c
new file mode 100644
index 00000000000..22913e7a181
--- /dev/null
+++ b/arch/blackfin/mm/isram-driver.c
@@ -0,0 +1,201 @@
+/*
+ * Description: Instruction SRAM accessor functions for the Blackfin
+ *
+ * Copyright 2008 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.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+
+#include <asm/blackfin.h>
+
+/*
+ * IMPORTANT WARNING ABOUT THESE FUNCTIONS
+ *
+ * The emulator will not function correctly if a write command is left in
+ * ITEST_COMMAND or DTEST_COMMAND AND access to cache memory is needed by
+ * the emulator. To avoid such problems, ensure that both ITEST_COMMAND
+ * and DTEST_COMMAND are zero when exiting these functions.
+ */
+
+
+/*
+ * On the Blackfin, L1 instruction sram (which operates at core speeds) can not
+ * be accessed by a normal core load, so we need to go through a few hoops to
+ * read/write it.
+ * To try to make it easier - we export a memcpy interface, where either src or
+ * dest can be in this special L1 memory area.
+ * The low level read/write functions should not be exposed to the rest of the
+ * kernel, since they operate on 64-bit data, and need specific address alignment
+ */
+
+static DEFINE_SPINLOCK(dtest_lock);
+
+/* Takes a void pointer */
+#define IADDR2DTEST(x) \
+ ({ unsigned long __addr = (unsigned long)(x); \
+ (__addr & 0x47F8) | /* address bits 14 & 10:3 */ \
+ (__addr & 0x0800) << 15 | /* address bit 11 */ \
+ (__addr & 0x3000) << 4 | /* address bits 13:12 */ \
+ (__addr & 0x8000) << 8 | /* address bit 15 */ \
+ (0x1000004); /* isram access */ \
+ })
+
+/* Takes a pointer, and returns the offset (in bits) which things should be shifted */
+#define ADDR2OFFSET(x) ((((unsigned long)(x)) & 0x7) * 8)
+
+/* Takes a pointer, determines if it is the last byte in the isram 64-bit data type */
+#define ADDR2LAST(x) ((((unsigned long)x) & 0x7) == 0x7)
+
+static void isram_write(const void *addr, uint64_t data)
+{
+ uint32_t cmd;
+ unsigned long flags;
+
+ if (addr >= (void *)(L1_CODE_START + L1_CODE_LENGTH))
+ return;
+
+ cmd = IADDR2DTEST(addr) | 1; /* write */
+
+ /*
+ * Writes to DTEST_DATA[0:1] need to be atomic with write to DTEST_COMMAND
+ * While in exception context - atomicity is guaranteed or double fault
+ */
+ spin_lock_irqsave(&dtest_lock, flags);
+
+ bfin_write_DTEST_DATA0(data & 0xFFFFFFFF);
+ bfin_write_DTEST_DATA1(data >> 32);
+
+ /* use the builtin, since interrupts are already turned off */
+ __builtin_bfin_csync();
+ bfin_write_DTEST_COMMAND(cmd);
+ __builtin_bfin_csync();
+
+ bfin_write_DTEST_COMMAND(0);
+ __builtin_bfin_csync();
+
+ spin_unlock_irqrestore(&dtest_lock, flags);
+}
+
+static uint64_t isram_read(const void *addr)
+{
+ uint32_t cmd;
+ unsigned long flags;
+ uint64_t ret;
+
+ if (addr > (void *)(L1_CODE_START + L1_CODE_LENGTH))
+ return 0;
+
+ cmd = IADDR2DTEST(addr) | 0; /* read */
+
+ /*
+ * Reads of DTEST_DATA[0:1] need to be atomic with write to DTEST_COMMAND
+ * While in exception context - atomicity is guaranteed or double fault
+ */
+ spin_lock_irqsave(&dtest_lock, flags);
+ /* use the builtin, since interrupts are already turned off */
+ __builtin_bfin_csync();
+ bfin_write_DTEST_COMMAND(cmd);
+ __builtin_bfin_csync();
+ ret = bfin_read_DTEST_DATA0() | ((uint64_t)bfin_read_DTEST_DATA1() << 32);
+
+ bfin_write_DTEST_COMMAND(0);
+ __builtin_bfin_csync();
+ spin_unlock_irqrestore(&dtest_lock, flags);
+
+ return ret;
+}
+
+static bool isram_check_addr(const void *addr, size_t n)
+{
+ if ((addr >= (void *)L1_CODE_START) &&
+ (addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))) {
+ if ((addr + n) >= (void *)(L1_CODE_START + L1_CODE_LENGTH)) {
+ show_stack(NULL, NULL);
+ printk(KERN_ERR "isram_memcpy: copy involving %p length "
+ "(%zu) too long\n", addr, n);
+ }
+ return true;
+ }
+ return false;
+}
+
+/*
+ * The isram_memcpy() function copies n bytes from memory area src to memory area dest.
+ * The isram_memcpy() function returns a pointer to dest.
+ * Either dest or src can be in L1 instruction sram.
+ */
+void *isram_memcpy(void *dest, const void *src, size_t n)
+{
+ uint64_t data_in = 0, data_out = 0;
+ size_t count;
+ bool dest_in_l1, src_in_l1, need_data, put_data;
+ unsigned char byte, *src_byte, *dest_byte;
+
+ src_byte = (unsigned char *)src;
+ dest_byte = (unsigned char *)dest;
+
+ dest_in_l1 = isram_check_addr(dest, n);
+ src_in_l1 = isram_check_addr(src, n);
+
+ need_data = true;
+ put_data = true;
+ for (count = 0; count < n; count++) {
+ if (src_in_l1) {
+ if (need_data) {
+ data_in = isram_read(src + count);
+ need_data = false;
+ }
+
+ if (ADDR2LAST(src + count))
+ need_data = true;
+
+ byte = (unsigned char)((data_in >> ADDR2OFFSET(src + count)) & 0xff);
+
+ } else {
+ /* src is in L2 or L3 - so just dereference*/
+ byte = src_byte[count];
+ }
+
+ if (dest_in_l1) {
+ if (put_data) {
+ data_out = isram_read(dest + count);
+ put_data = false;
+ }
+
+ data_out &= ~((uint64_t)0xff << ADDR2OFFSET(dest + count));
+ data_out |= ((uint64_t)byte << ADDR2OFFSET(dest + count));
+
+ if (ADDR2LAST(dest + count)) {
+ put_data = true;
+ isram_write(dest + count, data_out);
+ }
+ } else {
+ /* dest in L2 or L3 - so just dereference */
+ dest_byte[count] = byte;
+ }
+ }
+
+ /* make sure we dump the last byte if necessary */
+ if (dest_in_l1 && !put_data)
+ isram_write(dest + count, data_out);
+
+ return dest;
+}
+EXPORT_SYMBOL(isram_memcpy);
+
diff --git a/arch/blackfin/mm/blackfin_sram.c b/arch/blackfin/mm/sram-alloc.c
index 4f5e887a0d9..0f1ca6930c1 100644
--- a/arch/blackfin/mm/blackfin_sram.c
+++ b/arch/blackfin/mm/sram-alloc.c
@@ -1,13 +1,13 @@
/*
- * File: arch/blackfin/mm/blackfin_sram.c
+ * File: arch/blackfin/mm/sram-alloc.c
* Based on:
* Author:
*
* Created:
- * Description: SRAM driver for Blackfin ADSP-BF5xx
+ * Description: SRAM allocator for Blackfin L1 and L2 memory
*
* Modified:
- * Copyright 2004-2007 Analog Devices Inc.
+ * Copyright 2004-2008 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
@@ -78,7 +78,7 @@ static void __init l1sram_init(void)
free_l1_ssram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_ssram_head.next) {
- printk(KERN_INFO"Fail to initialize Scratchpad data SRAM.\n");
+ printk(KERN_INFO "Failed to initialize Scratchpad data SRAM\n");
return;
}
@@ -102,7 +102,7 @@ static void __init l1_data_sram_init(void)
free_l1_data_A_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_data_A_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L1 Data A SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L1 Data A SRAM\n");
return;
}
@@ -123,7 +123,7 @@ static void __init l1_data_sram_init(void)
free_l1_data_B_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_data_B_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L1 Data B SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L1 Data B SRAM\n");
return;
}
@@ -151,7 +151,7 @@ static void __init l1_inst_sram_init(void)
free_l1_inst_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_inst_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L1 Instruction SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L1 Instruction SRAM\n");
return;
}
@@ -179,7 +179,7 @@ static void __init l2_sram_init(void)
free_l2_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l2_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L2 SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L2 SRAM\n");
return;
}
@@ -351,28 +351,31 @@ static int _sram_free(const void *addr,
int sram_free(const void *addr)
{
- if (0) {}
+
#if L1_CODE_LENGTH != 0
- else if (addr >= (void *)L1_CODE_START
+ if (addr >= (void *)L1_CODE_START
&& addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))
return l1_inst_sram_free(addr);
+ else
#endif
#if L1_DATA_A_LENGTH != 0
- else if (addr >= (void *)L1_DATA_A_START
+ if (addr >= (void *)L1_DATA_A_START
&& addr < (void *)(L1_DATA_A_START + L1_DATA_A_LENGTH))
return l1_data_A_sram_free(addr);
+ else
#endif
#if L1_DATA_B_LENGTH != 0
- else if (addr >= (void *)L1_DATA_B_START
+ if (addr >= (void *)L1_DATA_B_START
&& addr < (void *)(L1_DATA_B_START + L1_DATA_B_LENGTH))
return l1_data_B_sram_free(addr);
+ else
#endif
#if L2_LENGTH != 0
- else if (addr >= (void *)L2_START
+ if (addr >= (void *)L2_START
&& addr < (void *)(L2_START + L2_LENGTH))
return l2_sram_free(addr);
-#endif
else
+#endif
return -1;
}
EXPORT_SYMBOL(sram_free);