Commit 80421fcc authored by Xiangfu Liu's avatar Xiangfu Liu Committed by Shinya Kuribayashi

MIPS: Ingenic XBurst Jz4740 processor support

Jz4740 is a multimedia application processor targeting for mobile
devices like e-Dictionary, eBook, portable media player (PMP) and
GPS navigator.  Jz4740 is powered by Ingenic 360 MHz XBurst CPU core
(JzRISC), in which RISC/SIMD/DSP hybrid instruction set architecture
provides high integration, high performance and low power consumption.

JzRISC incorporated in Jz4740 is the advanced and power-efficient
32-bit RISC core, compatible with MIPS32, with 16K I-Cache and 16K
D-Cache, and can operate at speeds up to 400 MHz.

On-chip modules such as LCD controller, embedded audio codec, multi-
channel SAR-ADC, AC97/I2S controller and camera I/F offer a rich
suite of peripherals for multimedia application.  NAND controller
(SLC/MLC), USB (host 1.1 and device 2.0), UART, I2C, SPI, etc. are
also available.

For more info about Ingenic XBurst Jz4740:
  http://en.ingenic.cn/eng/
  http://www.linux-mips.org/wiki/Ingenic

This patch introduces XBurst CPU support in U-Boot.  It's compatible
with MIPS32, but requires a bit different cache maintenance, timer
routines, and boot mechanism using USB boot tool, so XBurst support
can go into a separate new home, cpu/xburst/.
Signed-off-by: default avatarXiangfu Liu <xiangfu@openmobilefree.net>
Acked-by: default avatarDaniel <zpxu@ingenic.cn>
Signed-off-by: default avatarShinya Kuribayashi <skuribay@pobox.com>
parent 0841ca90
......@@ -181,6 +181,7 @@ Directory Hierarchy:
/mips Files generic to MIPS architecture
/cpu CPU specific files
/mips32 Files specific to MIPS32 CPUs
/xburst Files specific to Ingenic XBurst CPUs
/lib Architecture specific library files
/nios2 Files generic to Altera NIOS2 architecture
/cpu CPU specific files
......
#
# Copyright (C) 2011 Xiangfu Liu <xiangfu@openmobilefree.net>
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)lib$(CPU).o
START = start.o
SOBJS-y =
COBJS-y = cpu.o timer.o jz_serial.o
COBJS-$(CONFIG_JZ4740) += jz4740.o
SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
START := $(addprefix $(obj),$(START))
all: $(obj).depend $(START) $(LIB)
$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################
#
# Copyright (C) 2011 Xiangfu Liu <xiangfu@openmobilefree.net>
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
PLATFORM_CPPFLAGS += -march=mips32 -EL
PLATFORM_LDFLAGS += -EL
/*
* (C) Copyright 2003
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
* (C) Copyright 2011
* Xiangfu Liu <xiangfu@openmobilefree.net>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <command.h>
#include <netdev.h>
#include <asm/mipsregs.h>
#include <asm/cacheops.h>
#include <asm/reboot.h>
#include <asm/io.h>
#include <asm/jz4740.h>
#define cache_op(op, addr) \
__asm__ __volatile__( \
".set push\n" \
".set noreorder\n" \
".set mips3\n" \
"cache %0, %1\n" \
".set pop\n" \
: \
: "i" (op), "R" (*(unsigned char *)(addr)))
void __attribute__((weak)) _machine_restart(void)
{
struct jz4740_wdt *wdt = (struct jz4740_wdt *)JZ4740_WDT_BASE;
struct jz4740_tcu *tcu = (struct jz4740_tcu *)JZ4740_TCU_BASE;
u16 tmp;
/* wdt_select_extalclk() */
tmp = readw(&wdt->tcsr);
tmp &= ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN);
tmp |= WDT_TCSR_EXT_EN;
writew(tmp, &wdt->tcsr);
/* wdt_select_clk_div64() */
tmp = readw(&wdt->tcsr);
tmp &= ~WDT_TCSR_PRESCALE_MASK;
tmp |= WDT_TCSR_PRESCALE64,
writew(tmp, &wdt->tcsr);
writew(100, &wdt->tdr); /* wdt_set_data(100) */
writew(0, &wdt->tcnt); /* wdt_set_count(0); */
writew(TCU_TSSR_WDTSC, &tcu->tscr); /* tcu_start_wdt_clock */
writeb(readb(&wdt->tcer) | WDT_TCER_TCEN, &wdt->tcer); /* wdt start */
while (1)
;
}
int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
_machine_restart();
fprintf(stderr, "*** reset failed ***\n");
return 0;
}
void flush_cache(ulong start_addr, ulong size)
{
unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE;
unsigned long addr = start_addr & ~(lsize - 1);
unsigned long aend = (start_addr + size - 1) & ~(lsize - 1);
for (; addr <= aend; addr += lsize) {
cache_op(Hit_Writeback_Inv_D, addr);
cache_op(Hit_Invalidate_I, addr);
}
}
void flush_dcache_range(ulong start_addr, ulong stop)
{
unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE;
unsigned long addr = start_addr & ~(lsize - 1);
unsigned long aend = (stop - 1) & ~(lsize - 1);
for (; addr <= aend; addr += lsize)
cache_op(Hit_Writeback_Inv_D, addr);
}
void invalidate_dcache_range(ulong start_addr, ulong stop)
{
unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE;
unsigned long addr = start_addr & ~(lsize - 1);
unsigned long aend = (stop - 1) & ~(lsize - 1);
for (; addr <= aend; addr += lsize)
cache_op(Hit_Invalidate_D, addr);
}
void flush_icache_all(void)
{
u32 addr, t = 0;
__asm__ __volatile__("mtc0 $0, $28"); /* Clear Taglo */
__asm__ __volatile__("mtc0 $0, $29"); /* Clear TagHi */
for (addr = CKSEG0; addr < CKSEG0 + CONFIG_SYS_ICACHE_SIZE;
addr += CONFIG_SYS_CACHELINE_SIZE) {
cache_op(Index_Store_Tag_I, addr);
}
/* invalidate btb */
__asm__ __volatile__(
".set mips32\n\t"
"mfc0 %0, $16, 7\n\t"
"nop\n\t"
"ori %0,2\n\t"
"mtc0 %0, $16, 7\n\t"
".set mips2\n\t"
:
: "r" (t));
}
void flush_dcache_all(void)
{
u32 addr;
for (addr = CKSEG0; addr < CKSEG0 + CONFIG_SYS_DCACHE_SIZE;
addr += CONFIG_SYS_CACHELINE_SIZE) {
cache_op(Index_Writeback_Inv_D, addr);
}
__asm__ __volatile__("sync");
}
void flush_cache_all(void)
{
flush_dcache_all();
flush_icache_all();
}
/*
* Jz4740 common routines
* Copyright (c) 2006 Ingenic Semiconductor, <jlwei@ingenic.cn>
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <asm/jz4740.h>
void enable_interrupts(void)
{
}
int disable_interrupts(void)
{
return 0;
}
/*
* PLL output clock = EXTAL * NF / (NR * NO)
* NF = FD + 2, NR = RD + 2
* NO = 1 (if OD = 0), NO = 2 (if OD = 1 or 2), NO = 4 (if OD = 3)
*/
void pll_init(void)
{
struct jz4740_cpm *cpm = (struct jz4740_cpm *)JZ4740_CPM_BASE;
register unsigned int cfcr, plcr1;
int n2FR[33] = {
0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
9
};
int div[5] = {1, 3, 3, 3, 3}; /* divisors of I:S:P:L:M */
int nf, pllout2;
cfcr = CPM_CPCCR_CLKOEN |
CPM_CPCCR_PCS |
(n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) |
(n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) |
(n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) |
(n2FR[div[3]] << CPM_CPCCR_MDIV_BIT) |
(n2FR[div[4]] << CPM_CPCCR_LDIV_BIT);
pllout2 = (cfcr & CPM_CPCCR_PCS) ?
CONFIG_SYS_CPU_SPEED : (CONFIG_SYS_CPU_SPEED / 2);
/* Init USB Host clock, pllout2 must be n*48MHz */
writel(pllout2 / 48000000 - 1, &cpm->uhccdr);
nf = CONFIG_SYS_CPU_SPEED * 2 / CONFIG_SYS_EXTAL;
plcr1 = ((nf - 2) << CPM_CPPCR_PLLM_BIT) | /* FD */
(0 << CPM_CPPCR_PLLN_BIT) | /* RD=0, NR=2 */
(0 << CPM_CPPCR_PLLOD_BIT) | /* OD=0, NO=1 */
(0x20 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */
CPM_CPPCR_PLLEN; /* enable PLL */
/* init PLL */
writel(cfcr, &cpm->cpccr);
writel(plcr1, &cpm->cppcr);
}
void sdram_init(void)
{
struct jz4740_emc *emc = (struct jz4740_emc *)JZ4740_EMC_BASE;
register unsigned int dmcr0, dmcr, sdmode, tmp, cpu_clk, mem_clk, ns;
unsigned int cas_latency_sdmr[2] = {
EMC_SDMR_CAS_2,
EMC_SDMR_CAS_3,
};
unsigned int cas_latency_dmcr[2] = {
1 << EMC_DMCR_TCL_BIT, /* CAS latency is 2 */
2 << EMC_DMCR_TCL_BIT /* CAS latency is 3 */
};
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
cpu_clk = CONFIG_SYS_CPU_SPEED;
mem_clk = cpu_clk * div[__cpm_get_cdiv()] / div[__cpm_get_mdiv()];
writel(0, &emc->bcr); /* Disable bus release */
writew(0, &emc->rtcsr); /* Disable clock for counting */
/* Fault DMCR value for mode register setting*/
#define SDRAM_ROW0 11
#define SDRAM_COL0 8
#define SDRAM_BANK40 0
dmcr0 = ((SDRAM_ROW0 - 11) << EMC_DMCR_RA_BIT) |
((SDRAM_COL0 - 8) << EMC_DMCR_CA_BIT) |
(SDRAM_BANK40 << EMC_DMCR_BA_BIT) |
(SDRAM_BW16 << EMC_DMCR_BW_BIT) |
EMC_DMCR_EPIN |
cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)];
/* Basic DMCR value */
dmcr = ((SDRAM_ROW - 11) << EMC_DMCR_RA_BIT) |
((SDRAM_COL - 8) << EMC_DMCR_CA_BIT) |
(SDRAM_BANK4 << EMC_DMCR_BA_BIT) |
(SDRAM_BW16 << EMC_DMCR_BW_BIT) |
EMC_DMCR_EPIN |
cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)];
/* SDRAM timimg */
ns = 1000000000 / mem_clk;
tmp = SDRAM_TRAS / ns;
if (tmp < 4)
tmp = 4;
if (tmp > 11)
tmp = 11;
dmcr |= (tmp - 4) << EMC_DMCR_TRAS_BIT;
tmp = SDRAM_RCD / ns;
if (tmp > 3)
tmp = 3;
dmcr |= tmp << EMC_DMCR_RCD_BIT;
tmp = SDRAM_TPC / ns;
if (tmp > 7)
tmp = 7;
dmcr |= tmp << EMC_DMCR_TPC_BIT;
tmp = SDRAM_TRWL / ns;
if (tmp > 3)
tmp = 3;
dmcr |= tmp << EMC_DMCR_TRWL_BIT;
tmp = (SDRAM_TRAS + SDRAM_TPC) / ns;
if (tmp > 14)
tmp = 14;
dmcr |= ((tmp + 1) >> 1) << EMC_DMCR_TRC_BIT;
/* SDRAM mode value */
sdmode = EMC_SDMR_BT_SEQ |
EMC_SDMR_OM_NORMAL |
EMC_SDMR_BL_4 |
cas_latency_sdmr[((SDRAM_CASL == 3) ? 1 : 0)];
/* Stage 1. Precharge all banks by writing SDMR with DMCR.MRSET=0 */
writel(dmcr, &emc->dmcr);
writeb(0, JZ4740_EMC_SDMR0 | sdmode);
/* Wait for precharge, > 200us */
tmp = (cpu_clk / 1000000) * 1000;
while (tmp--)
;
/* Stage 2. Enable auto-refresh */
writel(dmcr | EMC_DMCR_RFSH, &emc->dmcr);
tmp = SDRAM_TREF / ns;
tmp = tmp / 64 + 1;
if (tmp > 0xff)
tmp = 0xff;
writew(tmp, &emc->rtcor);
writew(0, &emc->rtcnt);
/* Divisor is 64, CKO/64 */
writew(EMC_RTCSR_CKS_64, &emc->rtcsr);
/* Wait for number of auto-refresh cycles */
tmp = (cpu_clk / 1000000) * 1000;
while (tmp--)
;
/* Stage 3. Mode Register Set */
writel(dmcr0 | EMC_DMCR_RFSH | EMC_DMCR_MRSET, &emc->dmcr);
writeb(0, JZ4740_EMC_SDMR0 | sdmode);
/* Set back to basic DMCR value */
writel(dmcr | EMC_DMCR_RFSH | EMC_DMCR_MRSET, &emc->dmcr);
/* everything is ok now */
}
DECLARE_GLOBAL_DATA_PTR;
void calc_clocks(void)
{
unsigned int pllout;
unsigned int div[10] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
pllout = __cpm_get_pllout();
gd->cpu_clk = pllout / div[__cpm_get_cdiv()];
gd->sys_clk = pllout / div[__cpm_get_hdiv()];
gd->per_clk = pllout / div[__cpm_get_pdiv()];
gd->mem_clk = pllout / div[__cpm_get_mdiv()];
gd->dev_clk = CONFIG_SYS_EXTAL;
}
void rtc_init(void)
{
struct jz4740_rtc *rtc = (struct jz4740_rtc *)JZ4740_RTC_BASE;
while (!(readl(&rtc->rcr) & RTC_RCR_WRDY))
;
writel(readl(&rtc->rcr) | RTC_RCR_AE, &rtc->rcr); /* enable alarm */
while (!(readl(&rtc->rcr) & RTC_RCR_WRDY))
;
writel(0x00007fff, &rtc->rgr); /* type value */
while (!(readl(&rtc->rcr) & RTC_RCR_WRDY))
;
writel(0x0000ffe0, &rtc->hwfcr); /* Power on delay 2s */
while (!(readl(&rtc->rcr) & RTC_RCR_WRDY))
;
writel(0x00000fe0, &rtc->hrcr); /* reset delay 125ms */
}
/* U-Boot common routines */
phys_size_t initdram(int board_type)
{
struct jz4740_emc *emc = (struct jz4740_emc *)JZ4740_EMC_BASE;
u32 dmcr;
u32 rows, cols, dw, banks;
ulong size;
dmcr = readl(&emc->dmcr);
rows = 11 + ((dmcr & EMC_DMCR_RA_MASK) >> EMC_DMCR_RA_BIT);
cols = 8 + ((dmcr & EMC_DMCR_CA_MASK) >> EMC_DMCR_CA_BIT);
dw = (dmcr & EMC_DMCR_BW) ? 2 : 4;
banks = (dmcr & EMC_DMCR_BA) ? 4 : 2;
size = (1 << (rows + cols)) * dw * banks;
return size;
}
/*
* Jz4740 UART support
* Copyright (c) 2011
* Qi Hardware, Xiangfu Liu <xiangfu@sharism.cc>
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <asm/jz4740.h>
/*
* serial_init - initialize a channel
*
* This routine initializes the number of data bits, parity
* and set the selected baud rate. Interrupts are disabled.
* Set the modem control signals if the option is selected.
*
* RETURNS: N/A
*/
struct jz4740_uart *uart = (struct jz4740_uart *)CONFIG_SYS_UART_BASE;
int serial_init(void)
{
/* Disable port interrupts while changing hardware */
writeb(0, &uart->dlhr_ier);
/* Disable UART unit function */
writeb(~UART_FCR_UUE, &uart->iir_fcr);
/* Set both receiver and transmitter in UART mode (not SIR) */
writeb(~(SIRCR_RSIRE | SIRCR_TSIRE), &uart->isr);
/*
* Set databits, stopbits and parity.
* (8-bit data, 1 stopbit, no parity)
*/
writeb(UART_LCR_WLEN_8 | UART_LCR_STOP_1, &uart->lcr);
/* Set baud rate */
serial_setbrg();
/* Enable UART unit, enable and clear FIFO */
writeb(UART_FCR_UUE | UART_FCR_FE | UART_FCR_TFLS | UART_FCR_RFLS,
&uart->iir_fcr);
return 0;
}
void serial_setbrg(void)
{
u32 baud_div, tmp;
baud_div = CONFIG_SYS_EXTAL / 16 / CONFIG_BAUDRATE;
tmp = readb(&uart->lcr);
tmp |= UART_LCR_DLAB;
writeb(tmp, &uart->lcr);
writeb((baud_div >> 8) & 0xff, &uart->dlhr_ier);
writeb(baud_div & 0xff, &uart->rbr_thr_dllr);
tmp &= ~UART_LCR_DLAB;
writeb(tmp, &uart->lcr);
}
int serial_tstc(void)
{
if (readb(&uart->lsr) & UART_LSR_DR)
return 1;
return 0;
}
void serial_putc(const char c)
{
if (c == '\n')
serial_putc('\r');
/* Wait for fifo to shift out some bytes */
while (!((readb(&uart->lsr) & (UART_LSR_TDRQ | UART_LSR_TEMT)) == 0x60))
;
writeb((u8)c, &uart->rbr_thr_dllr);
}
int serial_getc(void)
{
while (!serial_tstc())
;
return readb(&uart->rbr_thr_dllr);
}
void serial_puts(const char *s)
{
while (*s)
serial_putc(*s++);
}
/*
* Startup Code for MIPS32 XBURST CPU-core
*
* Copyright (c) 2010 Xiangfu Liu <xiangfu@sharism.cc>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <version.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/cacheops.h>
.set noreorder
.globl _start
.text
_start:
/* Initialize $gp */
bal 1f
nop
.word _gp
1:
lw gp, 0(ra)