1aafd90912
revise anomaly handling by basing things on the compiler not the kconfig defines, so the header is stable and usable outside of the kernel. This also allows us to move some code from preprocessing to compiling (gcc culls dead code) which should help with code quality (readability, catch minor bugs, etc...). Signed-off-by: Mike Frysinger <michael.frysinger@analog.com> Signed-off-by: Bryan Wu <bryan.wu@analog.com>
434 lines
9.9 KiB
C
434 lines
9.9 KiB
C
/*
|
|
* Blackfin CPLB initialization
|
|
*
|
|
* Copyright 2004-2007 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/module.h>
|
|
|
|
#include <asm/blackfin.h>
|
|
#include <asm/cplbinit.h>
|
|
|
|
u_long icplb_table[MAX_CPLBS+1];
|
|
u_long dcplb_table[MAX_CPLBS+1];
|
|
|
|
#ifdef CONFIG_CPLB_SWITCH_TAB_L1
|
|
u_long ipdt_table[MAX_SWITCH_I_CPLBS+1]__attribute__((l1_data));
|
|
u_long dpdt_table[MAX_SWITCH_D_CPLBS+1]__attribute__((l1_data));
|
|
|
|
#ifdef CONFIG_CPLB_INFO
|
|
u_long ipdt_swapcount_table[MAX_SWITCH_I_CPLBS]__attribute__((l1_data));
|
|
u_long dpdt_swapcount_table[MAX_SWITCH_D_CPLBS]__attribute__((l1_data));
|
|
#endif /* CONFIG_CPLB_INFO */
|
|
|
|
#else
|
|
|
|
u_long ipdt_table[MAX_SWITCH_I_CPLBS+1];
|
|
u_long dpdt_table[MAX_SWITCH_D_CPLBS+1];
|
|
|
|
#ifdef CONFIG_CPLB_INFO
|
|
u_long ipdt_swapcount_table[MAX_SWITCH_I_CPLBS];
|
|
u_long dpdt_swapcount_table[MAX_SWITCH_D_CPLBS];
|
|
#endif /* CONFIG_CPLB_INFO */
|
|
|
|
#endif /*CONFIG_CPLB_SWITCH_TAB_L1*/
|
|
|
|
struct s_cplb {
|
|
struct cplb_tab init_i;
|
|
struct cplb_tab init_d;
|
|
struct cplb_tab switch_i;
|
|
struct cplb_tab switch_d;
|
|
};
|
|
|
|
#if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE)
|
|
static struct cplb_desc cplb_data[] = {
|
|
{
|
|
.start = 0,
|
|
.end = SIZE_1K,
|
|
.psize = SIZE_1K,
|
|
.attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB,
|
|
.i_conf = SDRAM_OOPS,
|
|
.d_conf = SDRAM_OOPS,
|
|
#if defined(CONFIG_DEBUG_HUNT_FOR_ZERO)
|
|
.valid = 1,
|
|
#else
|
|
.valid = 0,
|
|
#endif
|
|
.name = "ZERO Pointer Saveguard",
|
|
},
|
|
{
|
|
.start = L1_CODE_START,
|
|
.end = L1_CODE_START + L1_CODE_LENGTH,
|
|
.psize = SIZE_4M,
|
|
.attr = INITIAL_T | SWITCH_T | I_CPLB,
|
|
.i_conf = L1_IMEMORY,
|
|
.d_conf = 0,
|
|
.valid = 1,
|
|
.name = "L1 I-Memory",
|
|
},
|
|
{
|
|
.start = L1_DATA_A_START,
|
|
.end = L1_DATA_B_START + L1_DATA_B_LENGTH,
|
|
.psize = SIZE_4M,
|
|
.attr = INITIAL_T | SWITCH_T | D_CPLB,
|
|
.i_conf = 0,
|
|
.d_conf = L1_DMEMORY,
|
|
#if ((L1_DATA_A_LENGTH > 0) || (L1_DATA_B_LENGTH > 0))
|
|
.valid = 1,
|
|
#else
|
|
.valid = 0,
|
|
#endif
|
|
.name = "L1 D-Memory",
|
|
},
|
|
{
|
|
.start = 0,
|
|
.end = 0, /* dynamic */
|
|
.psize = 0,
|
|
.attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB,
|
|
.i_conf = SDRAM_IGENERIC,
|
|
.d_conf = SDRAM_DGENERIC,
|
|
.valid = 1,
|
|
.name = "SDRAM Kernel",
|
|
},
|
|
{
|
|
.start = 0, /* dynamic */
|
|
.end = 0, /* dynamic */
|
|
.psize = 0,
|
|
.attr = INITIAL_T | SWITCH_T | D_CPLB,
|
|
.i_conf = SDRAM_IGENERIC,
|
|
.d_conf = SDRAM_DNON_CHBL,
|
|
.valid = 1,
|
|
.name = "SDRAM RAM MTD",
|
|
},
|
|
{
|
|
.start = 0, /* dynamic */
|
|
.end = 0, /* dynamic */
|
|
.psize = SIZE_1M,
|
|
.attr = INITIAL_T | SWITCH_T | D_CPLB,
|
|
.d_conf = SDRAM_DNON_CHBL,
|
|
.valid = 1,
|
|
.name = "SDRAM Uncached DMA ZONE",
|
|
},
|
|
{
|
|
.start = 0, /* dynamic */
|
|
.end = 0, /* dynamic */
|
|
.psize = 0,
|
|
.attr = SWITCH_T | D_CPLB,
|
|
.i_conf = 0, /* dynamic */
|
|
.d_conf = 0, /* dynamic */
|
|
.valid = 1,
|
|
.name = "SDRAM Reserved Memory",
|
|
},
|
|
{
|
|
.start = ASYNC_BANK0_BASE,
|
|
.end = ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE,
|
|
.psize = 0,
|
|
.attr = SWITCH_T | D_CPLB,
|
|
.d_conf = SDRAM_EBIU,
|
|
.valid = 1,
|
|
.name = "ASYNC Memory",
|
|
},
|
|
{
|
|
#if defined(CONFIG_BF561)
|
|
.start = L2_SRAM,
|
|
.end = L2_SRAM_END,
|
|
.psize = SIZE_1M,
|
|
.attr = SWITCH_T | D_CPLB,
|
|
.i_conf = L2_MEMORY,
|
|
.d_conf = L2_MEMORY,
|
|
.valid = 1,
|
|
#else
|
|
.valid = 0,
|
|
#endif
|
|
.name = "L2 Memory",
|
|
}
|
|
};
|
|
|
|
static u16 __init lock_kernel_check(u32 start, u32 end)
|
|
{
|
|
if ((start <= (u32) _stext && end >= (u32) _end)
|
|
|| (start >= (u32) _stext && end <= (u32) _end))
|
|
return IN_KERNEL;
|
|
return 0;
|
|
}
|
|
|
|
static unsigned short __init
|
|
fill_cplbtab(struct cplb_tab *table,
|
|
unsigned long start, unsigned long end,
|
|
unsigned long block_size, unsigned long cplb_data)
|
|
{
|
|
int i;
|
|
|
|
switch (block_size) {
|
|
case SIZE_4M:
|
|
i = 3;
|
|
break;
|
|
case SIZE_1M:
|
|
i = 2;
|
|
break;
|
|
case SIZE_4K:
|
|
i = 1;
|
|
break;
|
|
case SIZE_1K:
|
|
default:
|
|
i = 0;
|
|
break;
|
|
}
|
|
|
|
cplb_data = (cplb_data & ~(3 << 16)) | (i << 16);
|
|
|
|
while ((start < end) && (table->pos < table->size)) {
|
|
|
|
table->tab[table->pos++] = start;
|
|
|
|
if (lock_kernel_check(start, start + block_size) == IN_KERNEL)
|
|
table->tab[table->pos++] =
|
|
cplb_data | CPLB_LOCK | CPLB_DIRTY;
|
|
else
|
|
table->tab[table->pos++] = cplb_data;
|
|
|
|
start += block_size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned short __init
|
|
close_cplbtab(struct cplb_tab *table)
|
|
{
|
|
|
|
while (table->pos < table->size) {
|
|
|
|
table->tab[table->pos++] = 0;
|
|
table->tab[table->pos++] = 0; /* !CPLB_VALID */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* helper function */
|
|
static void __fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end)
|
|
{
|
|
if (cplb_data[i].psize) {
|
|
fill_cplbtab(t,
|
|
cplb_data[i].start,
|
|
cplb_data[i].end,
|
|
cplb_data[i].psize,
|
|
cplb_data[i].i_conf);
|
|
} else {
|
|
#if defined(CONFIG_BLKFIN_CACHE)
|
|
if (ANOMALY_05000263 && i == SDRAM_KERN) {
|
|
fill_cplbtab(t,
|
|
cplb_data[i].start,
|
|
cplb_data[i].end,
|
|
SIZE_4M,
|
|
cplb_data[i].i_conf);
|
|
} else
|
|
#endif
|
|
{
|
|
fill_cplbtab(t,
|
|
cplb_data[i].start,
|
|
a_start,
|
|
SIZE_1M,
|
|
cplb_data[i].i_conf);
|
|
fill_cplbtab(t,
|
|
a_start,
|
|
a_end,
|
|
SIZE_4M,
|
|
cplb_data[i].i_conf);
|
|
fill_cplbtab(t, a_end,
|
|
cplb_data[i].end,
|
|
SIZE_1M,
|
|
cplb_data[i].i_conf);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void __fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end)
|
|
{
|
|
if (cplb_data[i].psize) {
|
|
fill_cplbtab(t,
|
|
cplb_data[i].start,
|
|
cplb_data[i].end,
|
|
cplb_data[i].psize,
|
|
cplb_data[i].d_conf);
|
|
} else {
|
|
fill_cplbtab(t,
|
|
cplb_data[i].start,
|
|
a_start, SIZE_1M,
|
|
cplb_data[i].d_conf);
|
|
fill_cplbtab(t, a_start,
|
|
a_end, SIZE_4M,
|
|
cplb_data[i].d_conf);
|
|
fill_cplbtab(t, a_end,
|
|
cplb_data[i].end,
|
|
SIZE_1M,
|
|
cplb_data[i].d_conf);
|
|
}
|
|
}
|
|
|
|
void __init generate_cpl_tables(void)
|
|
{
|
|
|
|
u16 i, j, process;
|
|
u32 a_start, a_end, as, ae, as_1m;
|
|
|
|
struct cplb_tab *t_i = NULL;
|
|
struct cplb_tab *t_d = NULL;
|
|
struct s_cplb cplb;
|
|
|
|
cplb.init_i.size = MAX_CPLBS;
|
|
cplb.init_d.size = MAX_CPLBS;
|
|
cplb.switch_i.size = MAX_SWITCH_I_CPLBS;
|
|
cplb.switch_d.size = MAX_SWITCH_D_CPLBS;
|
|
|
|
cplb.init_i.pos = 0;
|
|
cplb.init_d.pos = 0;
|
|
cplb.switch_i.pos = 0;
|
|
cplb.switch_d.pos = 0;
|
|
|
|
cplb.init_i.tab = icplb_table;
|
|
cplb.init_d.tab = dcplb_table;
|
|
cplb.switch_i.tab = ipdt_table;
|
|
cplb.switch_d.tab = dpdt_table;
|
|
|
|
cplb_data[SDRAM_KERN].end = memory_end;
|
|
|
|
#ifdef CONFIG_MTD_UCLINUX
|
|
cplb_data[SDRAM_RAM_MTD].start = memory_mtd_start;
|
|
cplb_data[SDRAM_RAM_MTD].end = memory_mtd_start + mtd_size;
|
|
cplb_data[SDRAM_RAM_MTD].valid = mtd_size > 0;
|
|
# if defined(CONFIG_ROMFS_FS)
|
|
cplb_data[SDRAM_RAM_MTD].attr |= I_CPLB;
|
|
|
|
/*
|
|
* The ROMFS_FS size is often not multiple of 1MB.
|
|
* This can cause multiple CPLB sets covering the same memory area.
|
|
* This will then cause multiple CPLB hit exceptions.
|
|
* Workaround: We ensure a contiguous memory area by extending the kernel
|
|
* memory section over the mtd section.
|
|
* For ROMFS_FS memory must be covered with ICPLBs anyways.
|
|
* So there is no difference between kernel and mtd memory setup.
|
|
*/
|
|
|
|
cplb_data[SDRAM_KERN].end = memory_mtd_start + mtd_size;;
|
|
cplb_data[SDRAM_RAM_MTD].valid = 0;
|
|
|
|
# endif
|
|
#else
|
|
cplb_data[SDRAM_RAM_MTD].valid = 0;
|
|
#endif
|
|
|
|
cplb_data[SDRAM_DMAZ].start = _ramend - DMA_UNCACHED_REGION;
|
|
cplb_data[SDRAM_DMAZ].end = _ramend;
|
|
|
|
cplb_data[RES_MEM].start = _ramend;
|
|
cplb_data[RES_MEM].end = physical_mem_end;
|
|
|
|
if (reserved_mem_dcache_on)
|
|
cplb_data[RES_MEM].d_conf = SDRAM_DGENERIC;
|
|
else
|
|
cplb_data[RES_MEM].d_conf = SDRAM_DNON_CHBL;
|
|
|
|
if (reserved_mem_icache_on)
|
|
cplb_data[RES_MEM].i_conf = SDRAM_IGENERIC;
|
|
else
|
|
cplb_data[RES_MEM].i_conf = SDRAM_INON_CHBL;
|
|
|
|
for (i = ZERO_P; i <= L2_MEM; i++) {
|
|
if (!cplb_data[i].valid)
|
|
continue;
|
|
|
|
as_1m = cplb_data[i].start % SIZE_1M;
|
|
|
|
/* We need to make sure all sections are properly 1M aligned
|
|
* However between Kernel Memory and the Kernel mtd section, depending on the
|
|
* rootfs size, there can be overlapping memory areas.
|
|
*/
|
|
|
|
if (as_1m && i != L1I_MEM && i != L1D_MEM) {
|
|
#ifdef CONFIG_MTD_UCLINUX
|
|
if (i == SDRAM_RAM_MTD) {
|
|
if ((cplb_data[SDRAM_KERN].end + 1) > cplb_data[SDRAM_RAM_MTD].start)
|
|
cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)) + SIZE_1M;
|
|
else
|
|
cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M));
|
|
} else
|
|
#endif
|
|
printk(KERN_WARNING "Unaligned Start of %s at 0x%X\n",
|
|
cplb_data[i].name, cplb_data[i].start);
|
|
}
|
|
|
|
as = cplb_data[i].start % SIZE_4M;
|
|
ae = cplb_data[i].end % SIZE_4M;
|
|
|
|
if (as)
|
|
a_start = cplb_data[i].start + (SIZE_4M - (as));
|
|
else
|
|
a_start = cplb_data[i].start;
|
|
|
|
a_end = cplb_data[i].end - ae;
|
|
|
|
for (j = INITIAL_T; j <= SWITCH_T; j++) {
|
|
|
|
switch (j) {
|
|
case INITIAL_T:
|
|
if (cplb_data[i].attr & INITIAL_T) {
|
|
t_i = &cplb.init_i;
|
|
t_d = &cplb.init_d;
|
|
process = 1;
|
|
} else
|
|
process = 0;
|
|
break;
|
|
case SWITCH_T:
|
|
if (cplb_data[i].attr & SWITCH_T) {
|
|
t_i = &cplb.switch_i;
|
|
t_d = &cplb.switch_d;
|
|
process = 1;
|
|
} else
|
|
process = 0;
|
|
break;
|
|
default:
|
|
process = 0;
|
|
break;
|
|
}
|
|
|
|
if (!process)
|
|
continue;
|
|
if (cplb_data[i].attr & I_CPLB)
|
|
__fill_code_cplbtab(t_i, i, a_start, a_end);
|
|
|
|
if (cplb_data[i].attr & D_CPLB)
|
|
__fill_data_cplbtab(t_d, i, a_start, a_end);
|
|
}
|
|
}
|
|
|
|
/* close tables */
|
|
|
|
close_cplbtab(&cplb.init_i);
|
|
close_cplbtab(&cplb.init_d);
|
|
|
|
cplb.init_i.tab[cplb.init_i.pos] = -1;
|
|
cplb.init_d.tab[cplb.init_d.pos] = -1;
|
|
cplb.switch_i.tab[cplb.switch_i.pos] = -1;
|
|
cplb.switch_d.tab[cplb.switch_d.pos] = -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|