2008-10-12 16:46:01 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 by Rob Purchase
|
2008-10-28 11:24:29 +00:00
|
|
|
* Copyright © 2008 Rafaël Carré
|
2008-10-12 16:46:01 +00:00
|
|
|
*
|
|
|
|
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "kernel.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "panic.h"
|
2008-11-10 19:53:12 +00:00
|
|
|
#include "ascodec-target.h"
|
2008-11-25 13:38:32 +00:00
|
|
|
#include "dma-target.h"
|
2008-12-04 20:04:31 +00:00
|
|
|
#include "clock-target.h"
|
2008-10-12 16:46:01 +00:00
|
|
|
|
|
|
|
#define default_interrupt(name) \
|
|
|
|
extern __attribute__((weak,alias("UIRQ"))) void name (void)
|
|
|
|
|
|
|
|
void irq_handler(void) __attribute__((interrupt ("IRQ"), naked));
|
|
|
|
void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked));
|
|
|
|
|
|
|
|
default_interrupt(INT_WATCHDOG);
|
|
|
|
default_interrupt(INT_TIMER1);
|
|
|
|
default_interrupt(INT_TIMER2);
|
|
|
|
default_interrupt(INT_USB);
|
|
|
|
default_interrupt(INT_DMAC);
|
|
|
|
default_interrupt(INT_NAND);
|
|
|
|
default_interrupt(INT_IDE);
|
|
|
|
default_interrupt(INT_MCI0);
|
|
|
|
default_interrupt(INT_MCI1);
|
|
|
|
default_interrupt(INT_AUDIO);
|
|
|
|
default_interrupt(INT_SSP);
|
|
|
|
default_interrupt(INT_I2C_MS);
|
|
|
|
default_interrupt(INT_I2C_AUDIO);
|
|
|
|
default_interrupt(INT_I2SIN);
|
|
|
|
default_interrupt(INT_I2SOUT);
|
|
|
|
default_interrupt(INT_UART);
|
|
|
|
default_interrupt(INT_GPIOD);
|
|
|
|
default_interrupt(RESERVED1); /* Interrupt 17 : unused */
|
|
|
|
default_interrupt(INT_CGU);
|
|
|
|
default_interrupt(INT_MEMORY_STICK);
|
|
|
|
default_interrupt(INT_DBOP);
|
|
|
|
default_interrupt(RESERVED2); /* Interrupt 21 : unused */
|
|
|
|
default_interrupt(RESERVED3); /* Interrupt 22 : unused */
|
|
|
|
default_interrupt(RESERVED4); /* Interrupt 23 : unused */
|
|
|
|
default_interrupt(RESERVED5); /* Interrupt 24 : unused */
|
|
|
|
default_interrupt(RESERVED6); /* Interrupt 25 : unused */
|
|
|
|
default_interrupt(RESERVED7); /* Interrupt 26 : unused */
|
|
|
|
default_interrupt(RESERVED8); /* Interrupt 27 : unused */
|
|
|
|
default_interrupt(RESERVED9); /* Interrupt 28 : unused */
|
|
|
|
default_interrupt(INT_GPIOA);
|
|
|
|
default_interrupt(INT_GPIOB);
|
|
|
|
default_interrupt(INT_GPIOC);
|
|
|
|
|
|
|
|
static const char * const irqname[] =
|
|
|
|
{
|
|
|
|
"INT_WATCHDOG", "INT_TIMER1", "INT_TIMER2", "INT_USB", "INT_DMAC", "INT_NAND",
|
|
|
|
"INT_IDE", "INT_MCI0", "INT_MCI1", "INT_AUDIO", "INT_SSP", "INT_I2C_MS",
|
|
|
|
"INT_I2C_AUDIO", "INT_I2SIN", "INT_I2SOUT", "INT_UART", "INT_GPIOD", "RESERVED1",
|
|
|
|
"INT_CGU", "INT_MEMORY_STICK", "INT_DBOP", "RESERVED2", "RESERVED3", "RESERVED4",
|
|
|
|
"RESERVED5", "RESERVED6", "RESERVED7", "RESERVED8", "RESERVED9", "INT_GPIOA",
|
|
|
|
"INT_GPIOB", "INT_GPIOC"
|
|
|
|
};
|
|
|
|
|
|
|
|
static void UIRQ(void)
|
|
|
|
{
|
2008-11-02 17:11:33 +00:00
|
|
|
unsigned int irq_no = 0;
|
|
|
|
int status = VIC_IRQ_STATUS;
|
|
|
|
while((status >>= 1))
|
|
|
|
irq_no++;
|
|
|
|
|
|
|
|
panicf("Unhandled IRQ %02X: %s", irq_no, irqname[irq_no]);
|
2008-10-12 16:46:01 +00:00
|
|
|
}
|
|
|
|
|
2008-12-05 17:10:11 +00:00
|
|
|
struct vec_int_src
|
2008-10-12 16:46:01 +00:00
|
|
|
{
|
2008-12-05 17:10:11 +00:00
|
|
|
int source;
|
|
|
|
void (*isr) (void);
|
|
|
|
};
|
2008-10-12 16:46:01 +00:00
|
|
|
|
2008-12-05 17:10:11 +00:00
|
|
|
/* Vectored interrupts (16 available) */
|
|
|
|
struct vec_int_src vec_int_srcs[] =
|
|
|
|
{
|
|
|
|
{ INT_SRC_TIMER1, INT_TIMER1 },
|
|
|
|
{ INT_SRC_TIMER2, INT_TIMER2 },
|
|
|
|
{ INT_SRC_DMAC, INT_DMAC },
|
|
|
|
{ INT_SRC_NAND, INT_NAND },
|
|
|
|
{ INT_SRC_MCI0, INT_MCI0 },
|
|
|
|
{ INT_SRC_GPIOA, INT_GPIOA, },
|
|
|
|
{ INT_SRC_GPIOB, INT_GPIOB, },
|
|
|
|
};
|
2008-10-12 16:46:01 +00:00
|
|
|
|
2008-12-05 17:10:11 +00:00
|
|
|
static void setup_vic(void)
|
|
|
|
{
|
|
|
|
volatile unsigned long *vic_vect_addrs = VIC_VECT_ADDRS;
|
|
|
|
volatile unsigned long *vic_vect_cntls = VIC_VECT_CNTLS;
|
|
|
|
const unsigned int n = sizeof(vec_int_srcs)/sizeof(vec_int_srcs[0]);
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
CGU_PERI |= CGU_VIC_CLOCK_ENABLE; /* enable VIC */
|
|
|
|
VIC_INT_EN_CLEAR = 0xffffffff; /* disable all interrupt lines */
|
|
|
|
VIC_INT_SELECT = 0; /* only IRQ, no FIQ */
|
2008-11-02 17:11:33 +00:00
|
|
|
|
2008-12-05 17:10:11 +00:00
|
|
|
VIC_DEF_VECT_ADDR = (unsigned long)UIRQ;
|
2008-10-25 19:13:11 +00:00
|
|
|
|
2008-12-05 17:10:11 +00:00
|
|
|
for(i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
vic_vect_addrs[i] = (unsigned long)vec_int_srcs[i].isr;
|
|
|
|
vic_vect_cntls[i] = (1<<5) | vec_int_srcs[i].source;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void irq_handler(void)
|
|
|
|
{
|
|
|
|
asm volatile( "stmfd sp!, {r0-r5,ip,lr} \n" /* Store context */
|
|
|
|
"ldr r5, =0xC6010030 \n" /* VIC_VECT_ADDR */
|
|
|
|
"mov lr, pc \n" /* Return from ISR */
|
|
|
|
"ldr pc, [r5] \n" /* execute ISR */
|
|
|
|
"str r0, [r5] \n" /* Ack interrupt */
|
|
|
|
"ldmfd sp!, {r0-r5,ip,lr} \n" /* Restore context */
|
|
|
|
"subs pc, lr, #4 \n" /* Return from IRQ */
|
|
|
|
);
|
2008-10-12 16:46:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fiq_handler(void)
|
|
|
|
{
|
|
|
|
asm volatile (
|
|
|
|
"subs pc, lr, #4 \r\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2008-11-09 06:17:21 +00:00
|
|
|
#ifdef BOOTLOADER
|
2008-10-28 11:24:29 +00:00
|
|
|
static void sdram_delay(void)
|
|
|
|
{
|
|
|
|
int delay = 1024; /* arbitrary */
|
|
|
|
while (delay--) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use the same initialization than OF */
|
|
|
|
static void sdram_init(void)
|
|
|
|
{
|
|
|
|
CGU_PERI |= (1<<26)|(1<<27); /* extmem & extmem intf clocks */
|
|
|
|
|
|
|
|
MPMC_CONTROL = 0x1; /* enable MPMC */
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_CONTROL = 0x183; /* SDRAM NOP, all clocks high */
|
|
|
|
sdram_delay();
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_CONTROL = 0x103; /* SDRAM PALL, all clocks high */
|
|
|
|
sdram_delay();
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_REFRESH = 0x138; /* 0x138 * 16 HCLK ticks between SDRAM refresh cycles */
|
|
|
|
|
|
|
|
MPMC_CONFIG = 0; /* little endian, HCLK:MPMCCLKOUT[3:0] ratio = 1:1 */
|
|
|
|
|
|
|
|
if(MPMC_PERIPH_ID2 & 0xf0)
|
|
|
|
MPMC_DYNAMIC_READ_CONFIG = 0x1; /* command delayed, clock out not delayed */
|
|
|
|
|
|
|
|
/* timings */
|
|
|
|
MPMC_DYNAMIC_tRP = 2;
|
|
|
|
MPMC_DYNAMIC_tRAS = 4;
|
|
|
|
MPMC_DYNAMIC_tSREX = 5;
|
|
|
|
MPMC_DYNAMIC_tAPR = 0;
|
|
|
|
MPMC_DYNAMIC_tDAL = 4;
|
|
|
|
MPMC_DYNAMIC_tWR = 2;
|
|
|
|
MPMC_DYNAMIC_tRC = 5;
|
|
|
|
MPMC_DYNAMIC_tRFC = 5;
|
|
|
|
MPMC_DYNAMIC_tXSR = 5;
|
|
|
|
MPMC_DYNAMIC_tRRD = 2;
|
|
|
|
MPMC_DYNAMIC_tMRD = 2;
|
|
|
|
|
2008-11-11 14:46:13 +00:00
|
|
|
#if defined(SANSA_CLIP) || defined(SANSA_M200V4)
|
|
|
|
/* 16 bits external bus, low power SDRAM, 16 Mbits = 2 Mbytes */
|
|
|
|
#define MEMORY_MODEL 0x21
|
|
|
|
|
|
|
|
#elif defined(SANSA_E200V2) || defined(SANSA_FUZE)
|
|
|
|
/* 16 bits external bus, high performance SDRAM, 64 Mbits = 8 Mbytes */
|
|
|
|
#define MEMORY_MODEL 0x5
|
2008-11-25 13:38:32 +00:00
|
|
|
|
2008-10-28 11:24:29 +00:00
|
|
|
#else
|
2008-11-11 14:46:13 +00:00
|
|
|
#error "The external memory in your player is unknown"
|
2008-10-28 11:24:29 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_RASCAS_0 = (2<<8)|2; /* CAS & RAS latency = 2 clock cycles */
|
|
|
|
MPMC_DYNAMIC_CONFIG_0 = (MEMORY_MODEL << 7);
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_RASCAS_1 = MPMC_DYNAMIC_CONFIG_1 =
|
|
|
|
MPMC_DYNAMIC_RASCAS_2 = MPMC_DYNAMIC_CONFIG_2 =
|
|
|
|
MPMC_DYNAMIC_RASCAS_3 = MPMC_DYNAMIC_CONFIG_3 = 0;
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_CONTROL = 0x82; /* SDRAM MODE, MPMCCLKOUT runs continuously */
|
|
|
|
|
|
|
|
/* this part is required, if you know why please explain */
|
|
|
|
unsigned int tmp = *(volatile unsigned int*)(0x30000000+0x2300*MEM);
|
|
|
|
(void)tmp; /* we just need to read from this location */
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_CONTROL = 0x2; /* SDRAM NORMAL, MPMCCLKOUT runs continuously */
|
|
|
|
|
|
|
|
MPMC_DYNAMIC_CONFIG_0 |= (1<<19); /* buffer enable */
|
|
|
|
}
|
2008-11-09 06:17:21 +00:00
|
|
|
#endif
|
2008-10-12 16:46:01 +00:00
|
|
|
|
|
|
|
void system_init(void)
|
|
|
|
{
|
2008-10-28 11:24:29 +00:00
|
|
|
|
2008-12-04 20:04:31 +00:00
|
|
|
#ifdef BOOTLOADER /* TODO: makes this work in the main build */
|
2008-11-02 00:34:44 +00:00
|
|
|
CGU_PROC = 0; /* fclk 24 MHz */
|
|
|
|
CGU_PERI &= ~0x7f; /* pclk 24 MHz */
|
|
|
|
|
2008-11-01 23:40:59 +00:00
|
|
|
asm volatile(
|
2008-11-02 00:34:44 +00:00
|
|
|
"mrc p15, 0, r0, c1, c0 \n"
|
|
|
|
"orr r0, r0, #0xC0000000 \n" /* asynchronous clocking */
|
|
|
|
"mcr p15, 0, r0, c1, c0 \n"
|
|
|
|
: : : "r0" );
|
|
|
|
|
2008-12-04 20:04:31 +00:00
|
|
|
CGU_PLLA = AS3525_PLLA_SETTING;
|
2008-11-03 22:48:56 +00:00
|
|
|
while(!(CGU_INTCTRL & (1<<0))); /* wait until PLLA is locked */
|
|
|
|
|
2008-12-04 20:04:31 +00:00
|
|
|
CGU_PROC = (AS3525_CPU_PREDIV << 2) | 1;
|
|
|
|
|
|
|
|
CGU_PERI |= ((CLK_DIV(AS3525_PLLA_FREQ, AS3525_PCLK_FREQ) - 1) << 2)
|
|
|
|
| 1; /* clk_in = PLLA */
|
2008-11-02 00:34:44 +00:00
|
|
|
|
|
|
|
asm volatile(
|
2008-11-01 23:40:59 +00:00
|
|
|
"mov r0, #0 \n"
|
|
|
|
"mcr p15, 0, r0, c7, c7 \n" /* invalidate icache & dcache */
|
|
|
|
"mrc p15, 0, r0, c1, c0 \n" /* control register */
|
|
|
|
"orr r0, r0, #0x1000 \n" /* enable icache */
|
|
|
|
"orr r0, r0, #4 \n" /* enable dcache */
|
|
|
|
"mcr p15, 0, r0, c1, c0 \n"
|
|
|
|
: : : "r0" );
|
|
|
|
|
2008-10-28 11:24:29 +00:00
|
|
|
sdram_init();
|
2008-12-04 20:04:31 +00:00
|
|
|
#endif /* BOOTLOADER */
|
|
|
|
|
|
|
|
#if 0 /* the GPIO clock is already enabled by the dualboot function */
|
|
|
|
CGU_PERI |= CGU_GPIO_CLOCK_ENABLE;
|
|
|
|
#endif
|
2008-11-02 00:34:44 +00:00
|
|
|
|
2008-11-06 02:31:32 +00:00
|
|
|
/* enable timer interface for TIMER1 & TIMER2 */
|
|
|
|
CGU_PERI |= CGU_TIMERIF_CLOCK_ENABLE;
|
|
|
|
|
2008-12-05 17:10:11 +00:00
|
|
|
setup_vic();
|
2008-12-04 20:04:31 +00:00
|
|
|
|
|
|
|
dma_init();
|
|
|
|
|
|
|
|
#ifndef BOOTLOADER
|
2008-11-25 13:38:32 +00:00
|
|
|
/* Disable fast hardware power-off, to use power button normally
|
|
|
|
* We don't need the power button in the bootloader. */
|
2008-11-16 22:36:13 +00:00
|
|
|
ascodec_init();
|
|
|
|
ascodec_write(AS3514_CVDD_DCDC3, ascodec_read(AS3514_CVDD_DCDC3) & (1<<2));
|
2008-12-04 20:04:31 +00:00
|
|
|
#endif /* !BOOTLOADER */
|
2008-11-25 13:38:32 +00:00
|
|
|
|
2008-11-30 16:36:32 +00:00
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
|
|
set_cpu_frequency(CPUFREQ_DEFAULT);
|
|
|
|
#endif
|
2008-10-12 16:46:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void system_reboot(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int system_memory_guard(int newmode)
|
|
|
|
{
|
|
|
|
(void)newmode;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-11-09 06:17:21 +00:00
|
|
|
|
2008-11-10 11:04:43 +00:00
|
|
|
#ifndef BOOTLOADER
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
|
|
void set_cpu_frequency(long frequency)
|
|
|
|
{
|
2008-11-26 23:19:13 +00:00
|
|
|
int divider = frequency ? (CPUFREQ_MAX / frequency) : 16 /* minimal */ ;
|
2008-11-26 16:46:12 +00:00
|
|
|
|
|
|
|
if(divider > 16)
|
|
|
|
divider = 16;
|
|
|
|
else if(divider < 1)
|
|
|
|
divider = 1;
|
|
|
|
|
|
|
|
cpu_frequency = CPUFREQ_MAX / divider;
|
|
|
|
|
|
|
|
CGU_PROC &= ~(0xf << 4) /* clear divider bits */ ^ ((divider-1) << 4);
|
2008-11-09 06:17:21 +00:00
|
|
|
}
|
2008-11-10 11:04:43 +00:00
|
|
|
#endif /* HAVE_ADJUSTABLE_CPU_FREQ */
|
|
|
|
#endif /* BOOTLOADER */
|