You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
freshtomato-arm/release/src-rt-6.x.4708/shared/min_osl.c

724 lines
16 KiB
C

/*
* Initialization and support routines for self-booting compressed
* image.
*
* Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id: min_osl.c 419467 2013-08-21 09:19:48Z $
*/
#include <typedefs.h>
#include <bcmdefs.h>
#include <osl.h>
#include <bcmdevs.h>
#include <bcmutils.h>
#include <siutils.h>
#include <hndcpu.h>
#include <sbchipc.h>
#include <hndchipc.h>
/* Global ASSERT type */
uint32 g_assert_type = 0;
#ifdef mips
/* Cache support */
/* Cache and line sizes */
uint __icache_size, __ic_lsize, __dcache_size, __dc_lsize;
static void
_change_cachability(uint32 cm)
{
uint32 prid, c0reg;
c0reg = MFC0(C0_CONFIG, 0);
c0reg &= ~CONF_CM_CMASK;
c0reg |= (cm & CONF_CM_CMASK);
MTC0(C0_CONFIG, 0, c0reg);
prid = MFC0(C0_PRID, 0);
if (BCM330X(prid)) {
c0reg = MFC0(C0_BROADCOM, 0);
/* Enable icache & dcache */
c0reg |= BRCM_IC_ENABLE | BRCM_DC_ENABLE;
MTC0(C0_BROADCOM, 0, c0reg);
}
}
static void (*change_cachability)(uint32);
void
caches_on(void)
{
uint32 config, config1, r2, tmp;
uint start, end, size, lsize;
config = MFC0(C0_CONFIG, 0);
r2 = config & CONF_AR;
config1 = MFC0(C0_CONFIG, 1);
icache_probe(config1, &size, &lsize);
__icache_size = size;
__ic_lsize = lsize;
dcache_probe(config1, &size, &lsize);
__dcache_size = size;
__dc_lsize = lsize;
/* If caches are not in the default state then
* presume that caches are already init'd
*/
if ((config & CONF_CM_CMASK) != CONF_CM_UNCACHED) {
blast_dcache();
blast_icache();
return;
}
tmp = R_REG(NULL, (uint32 *)(OSL_UNCACHED(SI_ENUM_BASE + CC_CHIPID)));
if (((tmp & CID_PKG_MASK) >> CID_PKG_SHIFT) != HDLSIM_PKG_ID) {
/* init icache */
start = KSEG0ADDR(caches_on) & 0xff800000;
end = (start + __icache_size);
MTC0(C0_TAGLO, 0, 0);
MTC0(C0_TAGHI, 0, 0);
while (start < end) {
cache_op(start, Index_Store_Tag_I);
start += __ic_lsize;
}
/* init dcache */
start = KSEG0ADDR(caches_on) & 0xff800000;
end = (start + __dcache_size);
if (r2) {
/* mips32r2 has the data tags in select 2 */
MTC0(C0_TAGLO, 2, 0);
MTC0(C0_TAGHI, 2, 0);
} else {
MTC0(C0_TAGLO, 0, 0);
MTC0(C0_TAGHI, 0, 0);
}
while (start < end) {
cache_op(start, Index_Store_Tag_D);
start += __dc_lsize;
}
}
/* Must be in KSEG1 to change cachability */
change_cachability = (void (*)(uint32))KSEG1ADDR(_change_cachability);
change_cachability(CONF_CM_CACHABLE_NONCOHERENT);
}
void
blast_dcache(void)
{
uint32 start, end;
start = KSEG0ADDR(blast_dcache) & 0xff800000;
end = start + __dcache_size;
while (start < end) {
cache_op(start, Index_Writeback_Inv_D);
start += __dc_lsize;
}
}
void
blast_icache(void)
{
uint32 start, end;
start = KSEG0ADDR(blast_icache) & 0xff800000;
end = start + __icache_size;
while (start < end) {
cache_op(start, Index_Invalidate_I);
start += __ic_lsize;
}
}
#elif defined(__ARM_ARCH_7A__)
static uint8 loader_pagetable_array[128*1024+16384];
typedef volatile struct scu_reg_struct_t {
uint32 control;
uint32 config;
uint32 cpupwrstatus;
uint32 invalidate;
uint32 rsvd1[4];
uint32 rsvd2[4];
uint32 rsvd3[4];
uint32 filtstart;
uint32 filtend;
uint32 rsvd4[2];
uint32 sac;
uint32 snsac;
} scu_reg_struct;
typedef volatile struct l2cc_reg_struct_t {
uint32 cache_id;
uint32 cache_type;
uint32 rsvd1[62];
uint32 control; /* 0x100 */
uint32 aux_control;
uint32 tag_ram_control;
uint32 data_ram_control;
uint32 rsvd2[60];
uint32 ev_counter_ctrl; /* 0x200 */
uint32 ev_counter1_cfg;
uint32 ev_counter0_cfg;
uint32 ev_counter1;
uint32 ev_counter0;
uint32 int_mask;
uint32 int_mask_status;
uint32 int_raw_status;
uint32 int_clear;
uint32 rsvd3[55];
uint32 rsvd4[64]; /* 0x300 */
uint32 rsvd5[64]; /* 0x400 */
uint32 rsvd6[64]; /* 0x500 */
uint32 rsvd7[64]; /* 0x600 */
uint32 rsvd8[12]; /* 0x700 - 0x72F */
uint32 cache_sync; /* 0x730 */
uint32 rsvd9[15];
uint32 inv_pa; /* 0x770 */
uint32 rsvd10[2];
uint32 inv_way; /* 0x77C */
uint32 rsvd11[12];
uint32 clean_pa; /* 0x7B0 */
uint32 rsvd12[1];
uint32 clean_index; /* 0x7B8 */
uint32 clean_way;
uint32 rsvd13[12];
uint32 clean_inv_pa; /* 0x7F0 */
uint32 rsvd14[1];
uint32 clean_inv_index;
uint32 clean_inv_way;
uint32 rsvd15[64]; /* 0x800 - 0x8FF */
uint32 d_lockdown0; /* 0x900 */
uint32 i_lockdown0;
uint32 d_lockdown1;
uint32 i_lockdown1;
uint32 d_lockdown2;
uint32 i_lockdown2;
uint32 d_lockdown3;
uint32 i_lockdown3;
uint32 d_lockdown4;
uint32 i_lockdown4;
uint32 d_lockdown5;
uint32 i_lockdown5;
uint32 d_lockdown6;
uint32 i_lockdown6;
uint32 d_lockdown7;
uint32 i_lockdown7;
uint32 rsvd16[4]; /* 0x940 */
uint32 lock_line_en; /* 0x950 */
uint32 unlock_way;
uint32 rsvd17[42];
uint32 rsvd18[64]; /* 0xA00 */
uint32 rsvd19[64]; /* 0xB00 */
uint32 addr_filtering_start; /* 0xC00 */
uint32 addr_filtering_end;
uint32 rsvd20[62];
uint32 rsvd21[64]; /* 0xD00 */
uint32 rsvd22[64]; /* 0xE00 */
uint32 rsvd23[16]; /* 0xF00 - 0xF3F */
uint32 debug_ctrl; /* 0xF40 */
uint32 rsvd24[7];
uint32 prefetch_ctrl; /* 0xF60 */
uint32 rsvd25[7];
uint32 power_ctrl; /* 0xF80 */
} l2cc_reg_struct;
/* ARM9 Private memory region */
#define IPROC_PERIPH_BASE (0x19020000) /* (IHOST_A9MP_scu_CONTROL) */
#define IPROC_PERIPH_SCU_REG_BASE (IPROC_PERIPH_BASE)
#define IPROC_L2CC_REG_BASE (IPROC_PERIPH_BASE + 0x2000) /* L2 Cache controller */
/* Structures and bit definitions */
/* SCU Control register */
#define IPROC_SCU_CTRL_SCU_EN (0x00000001)
#define IPROC_SCU_CTRL_ADRFLT_EN (0x00000002)
#define IPROC_SCU_CTRL_PARITY_EN (0x00000004)
#define IPROC_SCU_CTRL_SPEC_LNFL_EN (0x00000008)
#define IPROC_SCU_CTRL_FRC2P0_EN (0x00000010)
#define IPROC_SCU_CTRL_SCU_STNDBY_EN (0x00000020)
#define IPROC_SCU_CTRL_IC_STNDBY_EN (0x00000040)
/*
* CR1 bits (CP#15 CR1)
*/
#define CR_M (1 << 0) /* MMU enable */
#define CR_A (1 << 1) /* Alignment abort enable */
#define CR_C (1 << 2) /* Dcache enable */
#define CR_W (1 << 3) /* Write buffer enable */
#define CR_P (1 << 4) /* 32-bit exception handler */
#define CR_D (1 << 5) /* 32-bit data address range */
#define CR_L (1 << 6) /* Implementation defined */
#define CR_B (1 << 7) /* Big endian */
#define CR_S (1 << 8) /* System MMU protection */
#define CR_R (1 << 9) /* ROM MMU protection */
#define CR_F (1 << 10) /* Implementation defined */
#define CR_Z (1 << 11) /* Implementation defined */
#define CR_I (1 << 12) /* Icache enable */
#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */
#define CR_RR (1 << 14) /* Round Robin cache replacement */
#define CR_L4 (1 << 15) /* LDR pc can set T bit */
#define CR_DT (1 << 16)
#define CR_IT (1 << 18)
#define CR_ST (1 << 19)
#define CR_FI (1 << 21) /* Fast interrupt (lower latency mode) */
#define CR_U (1 << 22) /* Unaligned access operation */
#define CR_XP (1 << 23) /* Extended page tables */
#define CR_VE (1 << 24) /* Vectored interrupts */
#define CR_EE (1 << 25) /* Exception (Big) Endian */
#define CR_TRE (1 << 28) /* TEX remap enable */
#define CR_AFE (1 << 29) /* Access flag enable */
#define CR_TE (1 << 30) /* Thumb exception enable */
#define isb() __asm__ __volatile__ ("" : : : "memory")
#define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t")
extern void cpu_flush_cache_all(void);
extern void cpu_inv_cache_all(void);
void flush_cache(unsigned long dummy1, unsigned long dummy2)
{
cpu_flush_cache_all();
return;
}
static void l2cc_init(void)
{
uint32 regval;
l2cc_reg_struct *l2cc = (l2cc_reg_struct *)IPROC_L2CC_REG_BASE;
regval = l2cc->aux_control;
regval &= ~(0x000F0000); /* Clear the Way-size and associativity (8 way) */
regval |= 0x0A130000; /* Non-secure interrupt access, Way-size 16KB,
16 way and event monitoring
*/
l2cc->aux_control = regval;
l2cc->tag_ram_control = 0; /* Tag ram latency */
l2cc->data_ram_control = 0; /* Data ram latency */
}
static void l2cc_invalidate(void)
{
l2cc_reg_struct *l2cc = (l2cc_reg_struct *)IPROC_L2CC_REG_BASE;
/* Invalidate the entire L2 cache */
l2cc->inv_way = 0x0000FFFF;
}
int l2cc_enable(void)
{
int i;
l2cc_reg_struct *l2cc = (l2cc_reg_struct *)IPROC_L2CC_REG_BASE;
l2cc_init();
l2cc_invalidate();
i = 1000;
while (l2cc->inv_way && i)
{
--i;
};
if (i == 0)
return (-1);
/* Clear any pending interrupts from this controller */
l2cc->int_clear = 0x1FF;
/* Enable the L2 */
l2cc->control = 0x01;
/* mem barrier to sync up things */
i = 0;
asm("mcr p15, 0, %0, c7, c10, 4": :"r"(i));
return 0;
}
static void cp_delay(void)
{
volatile int i;
/* copro seems to need some delay between reading and writing */
for (i = 0; i < 1000; i++)
nop();
asm volatile("" : : : "memory");
}
void
caches_on(void)
{
int i;
uint32 val, *ptb, ptbaddr;
cpu_inv_cache_all();
/* Enable I$ */
asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc");
cp_delay();
val |= CR_I;
asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" : : "r" (val) : "cc");
isb();
/* prepare page table for MMU */
ptbaddr = (uint32)loader_pagetable_array;
/* Round down to next 64 kB limit */
ptbaddr += 0x10000;
ptbaddr &= ~(0x10000 - 1);
ptb = (uint32 *)ptbaddr;
/* Set up an identity-mapping for all 4GB, rw for everyone */
for (i = 0; i < 128; i++) {
/* DRAM area: TEX = 0x4, Ap = 3, Domain = 0, C =1, B = 0 */
ptb[i] = i << 20 | 0x4c0e;
}
for (i = 128; i < 480; i++) {
/* TEX = 0x2(device memory), Ap = 3, Domain = 0, C =0, B = 0 */
ptb[i] = i << 20 | 0x0c02;
}
for (i = 480; i < 512; i++) {
/* SPI region: TEX = 0x4, Ap = 3, Domain = 0, C =1, B = 0 */
ptb[i] = i << 20 | 0x4c0a;
}
for (i = 512; i < 4096; i++) {
/* TEX = 0x2(device memory), Ap = 3, Domain = 0, C =0, B = 0 */
ptb[i] = i << 20 | 0x2c02;
}
/* Apply page table address to CP15 */
asm volatile("mcr p15, 0, %0, c2, c0, 0" : : "r" (ptb) : "memory");
/* Set the access control to all-supervisor */
asm volatile("mcr p15, 0, %0, c3, c0, 0" : : "r" (~0));
/* Enable I$ and MMU */
asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc");
cp_delay();
val |= (CR_C | CR_M);
asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" : : "r" (val) : "cc");
isb();
}
void
blast_dcache(void)
{
#ifndef CFG_UNCACHED
uint32 val;
asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc");
cp_delay();
if ((val & CR_C) != CR_C)
return; /* D$ not enabled */
flush_cache(0, ~0);
#ifdef CFG_SHMOO
val &= ~CR_C;
#else
val &= ~(CR_C | CR_M);
#endif
asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" : : "r" (val) : "cc");
isb();
#endif /* !CFG_UNCACHED */
}
void
blast_icache(void)
{
#ifndef CFG_UNCACHED
uint32 val;
asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc");
cp_delay();
if ((val & CR_I) != CR_I)
return; /* I$ not enabled */
val &= ~CR_I;
asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" : : "r" (val) : "cc");
isb();
/* invalidate I-cache */
asm("mcr p15, 0, %0, c7, c5, 0": :"r" (0));
#endif
}
#endif /* mips */
/* uart output */
struct serial_struct {
unsigned char *reg_base;
unsigned short reg_shift;
int irq;
int baud_base;
};
static struct serial_struct min_uart;
#ifdef BCMDBG
#define LOG_BUF_LEN (16 * 1024)
#else
#define LOG_BUF_LEN (1024)
#endif
#define LOG_BUF_MASK (LOG_BUF_LEN-1)
static unsigned long log_idx;
static char log_buf[LOG_BUF_LEN];
static inline int
serial_in(struct serial_struct *info, int offset)
{
return ((int)R_REG(NULL, (uint8 *)(info->reg_base + (offset << info->reg_shift))));
}
static inline void
serial_out(struct serial_struct *info, int offset, int value)
{
W_REG(NULL, (uint8 *)(info->reg_base + (offset << info->reg_shift)), value);
}
void
putc(int c)
{
uint32 idx;
/* CR before LF */
if (c == '\n')
putc('\r');
/* Store in log buffer */
idx = *((uint32 *)OSL_UNCACHED((uintptr)&log_idx));
*((char *)OSL_UNCACHED(&log_buf[idx])) = (char)c;
*((uint32 *)OSL_UNCACHED((uintptr)&log_idx)) = (idx + 1) & LOG_BUF_MASK;
/* No UART */
if (!min_uart.reg_base)
return;
while (!(serial_in(&min_uart, UART_LSR) & UART_LSR_THRE));
serial_out(&min_uart, UART_TX, c);
}
/* assert & debugging */
/* general purpose memory allocation */
extern char text_start[], text_end[];
extern char data_start[], data_end[];
extern char bss_start[], bss_end[];
static ulong free_mem_ptr = 0;
static ulong free_mem_ptr_end = 0;
#define MIN_ALIGN 4 /* Alignment at 4 bytes */
#define MAX_ALIGN 4096 /* Max alignment at 4k */
void *
malloc(uint size)
{
return malloc_align(size, MIN_ALIGN);
}
void *
malloc_align(uint size, uint align_bits)
{
void *p;
uint align_mask;
/* Sanity check */
if (size < 0)
printf("Malloc error\n");
if (free_mem_ptr == 0)
printf("Memory error\n");
/* Align */
align_mask = 1 << align_bits;
if (align_mask < MIN_ALIGN)
align_mask = MIN_ALIGN;
if (align_mask > MAX_ALIGN)
align_mask = MAX_ALIGN;
align_mask--;
free_mem_ptr = (free_mem_ptr + align_mask) & ~align_mask;
p = (void *) free_mem_ptr;
free_mem_ptr += size;
if (free_mem_ptr >= free_mem_ptr_end)
printf("Out of memory\n");
return p;
}
int
free(void *where)
{
return 0;
}
/* get processor cycle count */
#if defined(mips)
#define get_cycle_count get_c0_count
#elif defined(__arm__) || defined(__thumb__) || defined(__thumb2__)
#define get_cycle_count get_arm_cyclecount
#ifdef __ARM_ARCH_7A__
extern long _getticks(void);
#define get_arm_cyclecount (uint32)_getticks
#endif
#endif /* mips */
uint32
osl_getcycles(void)
{
return get_cycle_count();
}
/* microsecond delay */
/* Default to 125 MHz */
static uint32 cpu_clock = 125000000;
static uint32 c0counts_per_us = 125000000 / 2000000;
static uint32 c0counts_per_ms = 125000000 / 2000;
void
udelay(uint32 us)
{
uint32 curr, lim;
curr = get_cycle_count();
lim = curr + (us * c0counts_per_us);
if (lim < curr)
while (get_cycle_count() > curr)
;
while (get_cycle_count() < lim)
;
}
#ifndef MIN_DO_TRAP
/* No trap handling in self-decompressing boots */
extern void trap_init(void);
void
trap_init(void)
{
}
#endif /* !MIN_DO_TRAP */
static void
serial_add(void *regs, uint irq, uint baud_base, uint reg_shift)
{
int quot;
if (min_uart.reg_base)
return;
min_uart.reg_base = regs;
min_uart.irq = irq;
min_uart.baud_base = baud_base / 16;
min_uart.reg_shift = reg_shift;
/* Set baud and 8N1 */
#if defined(CFG_SIM) && defined(__ARM_ARCH_7A__)
quot = (min_uart.baud_base + 300) / 600;
#else
quot = (min_uart.baud_base + 57600) / 115200;
#endif
serial_out(&min_uart, UART_LCR, UART_LCR_DLAB);
serial_out(&min_uart, UART_DLL, quot & 0xff);
serial_out(&min_uart, UART_DLM, quot >> 8);
serial_out(&min_uart, UART_LCR, UART_LCR_WLEN8);
/* According to the Synopsys website: "the serial clock
* modules must have time to see new register values
* and reset their respective state machines. This
* total time is guaranteed to be no more than
* (2 * baud divisor * 16) clock cycles of the slower
* of the two system clocks. No data should be transmitted
* or received before this maximum time expires."
*/
udelay(1000);
}
void *
osl_init()
{
uint32 c0counts_per_cycle;
si_t *sih;
/* Scan backplane */
sih = si_kattach(SI_OSH);
if (sih == NULL)
return NULL;
#if defined(mips)
si_mips_init(sih, 0);
c0counts_per_cycle = 2;
#elif defined(__arm__) || defined(__thumb__) || defined(__thumb2__)
si_arm_init(sih);
c0counts_per_cycle = 1;
#else
#error "Unknow CPU"
#endif
cpu_clock = si_cpu_clock(sih);
c0counts_per_us = cpu_clock / (1000000 * c0counts_per_cycle);
c0counts_per_ms = si_cpu_clock(sih) / (1000 * c0counts_per_cycle);
/* Don't really need to talk to the uart in simulation */
if ((sih->chippkg != HDLSIM_PKG_ID) && (sih->chippkg != HWSIM_PKG_ID))
si_serial_init(sih, serial_add);
/* Init malloc */
#if defined(CFG_SHMOO)
{
extern int _memsize;
if (_memsize) {
free_mem_ptr = _memsize >> 1;
free_mem_ptr_end = _memsize - (_memsize >> 2);
}
}
#else
free_mem_ptr = (ulong) bss_end;
free_mem_ptr_end = ((ulong)&sih) - 8192; /* Enough stack? */
#endif /* CFG_SHMOO */
return ((void *)sih);
}
/* translate bcmerros */
int
osl_error(int bcmerror)
{
if (bcmerror)
return -1;
else
return 0;
}