GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,81 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
extra-$(CONFIG_SPL_BUILD) := start.o
obj-y = clock.o mxs.o iomux.o timer.o
ifdef CONFIG_SPL_BUILD
obj-y += spl_boot.o spl_lradc_init.o spl_mem_init.o spl_power_init.o
endif
# Specify the target for use in elftosb call
MKIMAGE_TARGET-$(CONFIG_MX23) = mxsimage$(CONFIG_SPL_FRAMEWORK:%=-spl).mx23.cfg
MKIMAGE_TARGET-$(CONFIG_MX28) = mxsimage$(CONFIG_SPL_FRAMEWORK:%=-spl).mx28.cfg
# Generate HAB-capable IVT
#
# Note on computing the post-IVT size field value for the U-Boot binary.
# The value is the result of adding the following:
# -> The size of U-Boot binary aligned to 64B (u-boot.bin)
# -> The size of IVT block aligned to 64B (u-boot.ivt)
# -> The size of U-Boot signature (u-boot.sig), 3904 B
# -> The 64B hole in front of U-Boot binary for 'struct mxs_spl_data' passing
#
quiet_cmd_mkivt_mxs = MXSIVT $@
cmd_mkivt_mxs = \
sz=`expr \`stat -c "%s" $^\` + 64 + 3904 + 128` ; \
echo -n "0x402000d1 $2 0 0 0 $3 $4 0 $$sz 0 0 0 0 0 0 0" | \
tr -s " " | xargs -d " " -i printf "%08x\n" "{}" | rev | \
sed "s/\(.\)\(.\)/\\\\\\\\x\2\1\n/g" | xargs -i printf "{}" >$@
# Align binary to 64B
quiet_cmd_mkalign_mxs = MXSALGN $@
cmd_mkalign_mxs = \
dd if=$^ of=$@ ibs=64 conv=sync 2>/dev/null && \
mv $@ $^
# Assemble the CSF file
quiet_cmd_mkcsfreq_mxs = MXSCSFR $@
cmd_mkcsfreq_mxs = \
ivt=$(word 1,$^) ; \
bin=$(word 2,$^) ; \
csf=$(word 3,$^) ; \
sed "s@VENDOR@$(VENDOR)@g;s@BOARD@$(BOARD)@g" "$$csf" | \
sed '/^\#\#Blocks/ d' > $@ ; \
echo " Blocks = $2 0x0 `stat -c '%s' $$bin` \"$$bin\" , \\" >> $@ ; \
echo " $3 0x0 0x40 \"$$ivt\"" >> $@
# Sign files
quiet_cmd_mkcst_mxs = MXSCST $@
cmd_mkcst_mxs = cst -o $@ < $^ \
$(if $(KBUILD_VERBOSE:1=), >/dev/null)
spl/u-boot-spl.ivt: spl/u-boot-spl.bin
$(call if_changed,mkalign_mxs)
$(call if_changed,mkivt_mxs,$(CONFIG_SPL_TEXT_BASE),\
0x00008000,0x00008040)
u-boot.ivt: u-boot.bin
$(call if_changed,mkalign_mxs)
$(call if_changed,mkivt_mxs,$(CONFIG_SYS_TEXT_BASE),\
0x40001000,0x40001040)
spl/u-boot-spl.csf: spl/u-boot-spl.ivt spl/u-boot-spl.bin board/$(VENDOR)/$(BOARD)/sign/u-boot-spl.csf
$(call if_changed,mkcsfreq_mxs,$(CONFIG_SPL_TEXT_BASE),0x8000)
u-boot.csf: u-boot.ivt u-boot.bin board/$(VENDOR)/$(BOARD)/sign/u-boot.csf
$(call if_changed,mkcsfreq_mxs,$(CONFIG_SYS_TEXT_BASE),0x40001000)
%.sig: %.csf
$(call if_changed,mkcst_mxs)
MKIMAGEFLAGS_u-boot.sb = -n $< -T mxsimage
u-boot.sb: $(src)/$(MKIMAGE_TARGET-y) u-boot.bin spl/u-boot-spl.bin FORCE
$(call if_changed,mkimage)
MKIMAGEFLAGS_u-boot-signed.sb = -n $< -T mxsimage
u-boot-signed.sb: $(src)/mxsimage-signed.cfg u-boot.ivt u-boot.sig spl/u-boot-spl.ivt spl/u-boot-spl.sig FORCE
$(call if_changed,mkimage)
@@ -0,0 +1,435 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX23/i.MX28 clock setup code
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* Copyright (C) 2010 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
/*
* The PLL frequency is 480MHz and XTAL frequency is 24MHz
* iMX23: datasheet section 4.2
* iMX28: datasheet section 10.2
*/
#define PLL_FREQ_KHZ 480000
#define PLL_FREQ_COEF 18
#define XTAL_FREQ_KHZ 24000
#define PLL_FREQ_MHZ (PLL_FREQ_KHZ / 1000)
#define XTAL_FREQ_MHZ (XTAL_FREQ_KHZ / 1000)
#if defined(CONFIG_MX23)
#define MXC_SSPCLK_MAX MXC_SSPCLK0
#elif defined(CONFIG_MX28)
#define MXC_SSPCLK_MAX MXC_SSPCLK3
#endif
static uint32_t mxs_get_pclk(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t clkctrl, clkseq, div;
uint8_t clkfrac, frac;
clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu);
/* No support of fractional divider calculation */
if (clkctrl &
(CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
return 0;
}
clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
/* XTAL Path */
if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
CLKCTRL_CPU_DIV_XTAL_OFFSET;
return XTAL_FREQ_MHZ / div;
}
/* REF Path */
clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
}
static uint32_t mxs_get_hclk(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t div;
uint32_t clkctrl;
clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus);
/* No support of fractional divider calculation */
if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN)
return 0;
div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
return mxs_get_pclk() / div;
}
static uint32_t mxs_get_emiclk(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t clkctrl, clkseq, div;
uint8_t clkfrac, frac;
clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
clkctrl = readl(&clkctrl_regs->hw_clkctrl_emi);
/* XTAL Path */
if (clkseq & CLKCTRL_CLKSEQ_BYPASS_EMI) {
div = (clkctrl & CLKCTRL_EMI_DIV_XTAL_MASK) >>
CLKCTRL_EMI_DIV_XTAL_OFFSET;
return XTAL_FREQ_MHZ / div;
}
/* REF Path */
clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]);
frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
div = clkctrl & CLKCTRL_EMI_DIV_EMI_MASK;
return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
}
static uint32_t mxs_get_gpmiclk(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
#if defined(CONFIG_MX23)
uint8_t *reg =
&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU];
#elif defined(CONFIG_MX28)
uint8_t *reg =
&clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_GPMI];
#endif
uint32_t clkctrl, clkseq, div;
uint8_t clkfrac, frac;
clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
clkctrl = readl(&clkctrl_regs->hw_clkctrl_gpmi);
/* XTAL Path */
if (clkseq & CLKCTRL_CLKSEQ_BYPASS_GPMI) {
div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
return XTAL_FREQ_MHZ / div;
}
/* REF Path */
clkfrac = readb(reg);
frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
}
/*
* Set IO clock frequency, in kHz
*/
void mxs_set_ioclk(enum mxs_ioclock io, uint32_t freq)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t div;
int io_reg;
if (freq == 0)
return;
if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
return;
div = (PLL_FREQ_KHZ * PLL_FREQ_COEF) / freq;
if (div < 18)
div = 18;
if (div > 35)
div = 35;
io_reg = CLKCTRL_FRAC0_IO0 - io; /* Register order is reversed */
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac0_set[io_reg]);
writeb(CLKCTRL_FRAC_CLKGATE | (div & CLKCTRL_FRAC_FRAC_MASK),
&clkctrl_regs->hw_clkctrl_frac0[io_reg]);
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac0_clr[io_reg]);
}
/*
* Get IO clock, returns IO clock in kHz
*/
static uint32_t mxs_get_ioclk(enum mxs_ioclock io)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint8_t ret;
int io_reg;
if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
return 0;
io_reg = CLKCTRL_FRAC0_IO0 - io; /* Register order is reversed */
ret = readb(&clkctrl_regs->hw_clkctrl_frac0[io_reg]) &
CLKCTRL_FRAC_FRAC_MASK;
return (PLL_FREQ_KHZ * PLL_FREQ_COEF) / ret;
}
/*
* Configure SSP clock frequency, in kHz
*/
void mxs_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t clk, clkreg;
if (ssp > MXC_SSPCLK_MAX)
return;
clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
(ssp * sizeof(struct mxs_register_32));
clrbits_le32(clkreg, CLKCTRL_SSP_CLKGATE);
while (readl(clkreg) & CLKCTRL_SSP_CLKGATE)
;
if (xtal)
clk = XTAL_FREQ_KHZ;
else
clk = mxs_get_ioclk(ssp >> 1);
if (freq > clk)
return;
/* Calculate the divider and cap it if necessary */
clk /= freq;
if (clk > CLKCTRL_SSP_DIV_MASK)
clk = CLKCTRL_SSP_DIV_MASK;
clrsetbits_le32(clkreg, CLKCTRL_SSP_DIV_MASK, clk);
while (readl(clkreg) & CLKCTRL_SSP_BUSY)
;
if (xtal)
writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
&clkctrl_regs->hw_clkctrl_clkseq_set);
else
writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
&clkctrl_regs->hw_clkctrl_clkseq_clr);
}
/*
* Return SSP frequency, in kHz
*/
static uint32_t mxs_get_sspclk(enum mxs_sspclock ssp)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t clkreg;
uint32_t clk, tmp;
if (ssp > MXC_SSPCLK_MAX)
return 0;
tmp = readl(&clkctrl_regs->hw_clkctrl_clkseq);
if (tmp & (CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp))
return XTAL_FREQ_KHZ;
clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
(ssp * sizeof(struct mxs_register_32));
tmp = readl(clkreg) & CLKCTRL_SSP_DIV_MASK;
if (tmp == 0)
return 0;
clk = mxs_get_ioclk(ssp >> 1);
return clk / tmp;
}
/*
* Set SSP/MMC bus frequency, in kHz)
*/
void mxs_set_ssp_busclock(unsigned int bus, uint32_t freq)
{
struct mxs_ssp_regs *ssp_regs;
const enum mxs_sspclock clk = mxs_ssp_clock_by_bus(bus);
const uint32_t sspclk = mxs_get_sspclk(clk);
uint32_t reg;
uint32_t divide, rate, tgtclk;
ssp_regs = mxs_ssp_regs_by_bus(bus);
/*
* SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
* CLOCK_DIVIDE has to be an even value from 2 to 254, and
* CLOCK_RATE could be any integer from 0 to 255.
*/
for (divide = 2; divide < 254; divide += 2) {
rate = sspclk / freq / divide;
if (rate <= 256)
break;
}
tgtclk = sspclk / divide / rate;
while (tgtclk > freq) {
rate++;
tgtclk = sspclk / divide / rate;
}
if (rate > 256)
rate = 256;
/* Always set timeout the maximum */
reg = SSP_TIMING_TIMEOUT_MASK |
(divide << SSP_TIMING_CLOCK_DIVIDE_OFFSET) |
((rate - 1) << SSP_TIMING_CLOCK_RATE_OFFSET);
writel(reg, &ssp_regs->hw_ssp_timing);
debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n",
bus, tgtclk, freq);
}
void mxs_set_lcdclk(uint32_t __maybe_unused lcd_base, uint32_t freq)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
uint32_t fp, x, k_rest, k_best, x_best, tk;
int32_t k_best_l = 999, k_best_t = 0, x_best_l = 0xff, x_best_t = 0xff;
if (freq == 0)
return;
#if defined(CONFIG_MX23)
writel(CLKCTRL_CLKSEQ_BYPASS_PIX, &clkctrl_regs->hw_clkctrl_clkseq_clr);
#elif defined(CONFIG_MX28)
writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF, &clkctrl_regs->hw_clkctrl_clkseq_clr);
#endif
/*
* / 18 \ 1 1
* freq kHz = | 480000000 Hz * -- | * --- * ------
* \ x / k 1000
*
* 480000000 Hz 18
* ------------ * --
* freq kHz x
* k = -------------------
* 1000
*/
fp = ((PLL_FREQ_KHZ * 1000) / freq) * 18;
for (x = 18; x <= 35; x++) {
tk = fp / x;
if ((tk / 1000 == 0) || (tk / 1000 > 255))
continue;
k_rest = tk % 1000;
if (k_rest < (k_best_l % 1000)) {
k_best_l = tk;
x_best_l = x;
}
if (k_rest > (k_best_t % 1000)) {
k_best_t = tk;
x_best_t = x;
}
}
if (1000 - (k_best_t % 1000) > (k_best_l % 1000)) {
k_best = k_best_l;
x_best = x_best_l;
} else {
k_best = k_best_t;
x_best = x_best_t;
}
k_best /= 1000;
#if defined(CONFIG_MX23)
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_PIX]);
writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_PIX]);
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_PIX]);
writel(CLKCTRL_PIX_CLKGATE,
&clkctrl_regs->hw_clkctrl_pix_set);
clrsetbits_le32(&clkctrl_regs->hw_clkctrl_pix,
CLKCTRL_PIX_DIV_MASK | CLKCTRL_PIX_CLKGATE,
k_best << CLKCTRL_PIX_DIV_OFFSET);
while (readl(&clkctrl_regs->hw_clkctrl_pix) & CLKCTRL_PIX_BUSY)
;
#elif defined(CONFIG_MX28)
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac1_set[CLKCTRL_FRAC1_PIX]);
writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
&clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_PIX]);
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac1_clr[CLKCTRL_FRAC1_PIX]);
writel(CLKCTRL_DIS_LCDIF_CLKGATE,
&clkctrl_regs->hw_clkctrl_lcdif_set);
clrsetbits_le32(&clkctrl_regs->hw_clkctrl_lcdif,
CLKCTRL_DIS_LCDIF_DIV_MASK | CLKCTRL_DIS_LCDIF_CLKGATE,
k_best << CLKCTRL_DIS_LCDIF_DIV_OFFSET);
while (readl(&clkctrl_regs->hw_clkctrl_lcdif) & CLKCTRL_DIS_LCDIF_BUSY)
;
#endif
}
uint32_t mxc_get_clock(enum mxc_clock clk)
{
switch (clk) {
case MXC_ARM_CLK:
return mxs_get_pclk() * 1000000;
case MXC_GPMI_CLK:
return mxs_get_gpmiclk() * 1000000;
case MXC_AHB_CLK:
case MXC_IPG_CLK:
return mxs_get_hclk() * 1000000;
case MXC_EMI_CLK:
return mxs_get_emiclk();
case MXC_IO0_CLK:
return mxs_get_ioclk(MXC_IOCLK0);
case MXC_IO1_CLK:
return mxs_get_ioclk(MXC_IOCLK1);
case MXC_XTAL_CLK:
return XTAL_FREQ_KHZ * 1000;
case MXC_SSP0_CLK:
return mxs_get_sspclk(MXC_SSPCLK0);
#ifdef CONFIG_MX28
case MXC_SSP1_CLK:
return mxs_get_sspclk(MXC_SSPCLK1);
case MXC_SSP2_CLK:
return mxs_get_sspclk(MXC_SSPCLK2);
case MXC_SSP3_CLK:
return mxs_get_sspclk(MXC_SSPCLK3);
#endif
}
return 0;
}
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2004-2006,2010 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
* Copyright (C) 2009 by Jan Weitzel Phytec Messtechnik GmbH,
* <armlinux@phytec.de>
*/
#include <common.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/iomux.h>
#include <asm/arch/imx-regs.h>
#if defined(CONFIG_MX23)
#define DRIVE_OFFSET 0x200
#define PULL_OFFSET 0x400
#elif defined(CONFIG_MX28)
#define DRIVE_OFFSET 0x300
#define PULL_OFFSET 0x600
#else
#error "Please select CONFIG_MX23 or CONFIG_MX28"
#endif
/*
* configures a single pad in the iomuxer
*/
int mxs_iomux_setup_pad(iomux_cfg_t pad)
{
u32 reg, ofs, bp, bm;
void *iomux_base = (void *)MXS_PINCTRL_BASE;
struct mxs_register_32 *mxs_reg;
/* muxsel */
ofs = 0x100;
ofs += PAD_BANK(pad) * 0x20 + PAD_PIN(pad) / 16 * 0x10;
bp = PAD_PIN(pad) % 16 * 2;
bm = 0x3 << bp;
reg = readl(iomux_base + ofs);
reg &= ~bm;
reg |= PAD_MUXSEL(pad) << bp;
writel(reg, iomux_base + ofs);
/* drive */
ofs = DRIVE_OFFSET;
ofs += PAD_BANK(pad) * 0x40 + PAD_PIN(pad) / 8 * 0x10;
/* mA */
if (PAD_MA_VALID(pad)) {
bp = PAD_PIN(pad) % 8 * 4;
bm = 0x3 << bp;
reg = readl(iomux_base + ofs);
reg &= ~bm;
reg |= PAD_MA(pad) << bp;
writel(reg, iomux_base + ofs);
}
/* vol */
if (PAD_VOL_VALID(pad)) {
bp = PAD_PIN(pad) % 8 * 4 + 2;
mxs_reg = (struct mxs_register_32 *)(iomux_base + ofs);
if (PAD_VOL(pad))
writel(1 << bp, &mxs_reg->reg_set);
else
writel(1 << bp, &mxs_reg->reg_clr);
}
/* pull */
if (PAD_PULL_VALID(pad)) {
ofs = PULL_OFFSET;
ofs += PAD_BANK(pad) * 0x10;
bp = PAD_PIN(pad);
mxs_reg = (struct mxs_register_32 *)(iomux_base + ofs);
if (PAD_PULL(pad))
writel(1 << bp, &mxs_reg->reg_set);
else
writel(1 << bp, &mxs_reg->reg_clr);
}
return 0;
}
int mxs_iomux_setup_multiple_pads(const iomux_cfg_t *pad_list, unsigned count)
{
const iomux_cfg_t *p = pad_list;
int i;
int ret;
for (i = 0; i < count; i++) {
ret = mxs_iomux_setup_pad(*p);
if (ret)
return ret;
p++;
}
return 0;
}
@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX23/i.MX28 common code
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* Copyright (C) 2010 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/mach-imx/dma.h>
#include <asm/arch/gpio.h>
#include <asm/arch/iomux.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <linux/compiler.h>
DECLARE_GLOBAL_DATA_PTR;
/* Lowlevel init isn't used on i.MX28, so just have a dummy here */
__weak void lowlevel_init(void) {}
void reset_cpu(ulong ignored) __attribute__((noreturn));
void reset_cpu(ulong ignored)
{
struct mxs_rtc_regs *rtc_regs =
(struct mxs_rtc_regs *)MXS_RTC_BASE;
struct mxs_lcdif_regs *lcdif_regs =
(struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
/*
* Shut down the LCD controller as it interferes with BootROM boot mode
* pads sampling.
*/
writel(LCDIF_CTRL_RUN, &lcdif_regs->hw_lcdif_ctrl_clr);
/* Wait 1 uS before doing the actual watchdog reset */
writel(1, &rtc_regs->hw_rtc_watchdog);
writel(RTC_CTRL_WATCHDOGEN, &rtc_regs->hw_rtc_ctrl_set);
/* Endless loop, reset will exit from here */
for (;;)
;
}
/*
* This function will craft a jumptable at 0x0 which will redirect interrupt
* vectoring to proper location of U-Boot in RAM.
*
* The structure of the jumptable will be as follows:
* ldr pc, [pc, #0x18] ..... for each vector, thus repeated 8 times
* <destination address> ... for each previous ldr, thus also repeated 8 times
*
* The "ldr pc, [pc, #0x18]" instruction above loads address from memory at
* offset 0x18 from current value of PC register. Note that PC is already
* incremented by 4 when computing the offset, so the effective offset is
* actually 0x20, this the associated <destination address>. Loading the PC
* register with an address performs a jump to that address.
*/
void mx28_fixup_vt(uint32_t start_addr)
{
/* ldr pc, [pc, #0x18] */
const uint32_t ldr_pc = 0xe59ff018;
/* Jumptable location is 0x0 */
uint32_t *vt = (uint32_t *)0x0;
int i;
for (i = 0; i < 8; i++) {
/* cppcheck-suppress nullPointer */
vt[i] = ldr_pc;
/* cppcheck-suppress nullPointer */
vt[i + 8] = start_addr + (4 * i);
}
}
#ifdef CONFIG_ARCH_MISC_INIT
int arch_misc_init(void)
{
mx28_fixup_vt(gd->relocaddr);
return 0;
}
#endif
int arch_cpu_init(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
extern uint32_t _start;
mx28_fixup_vt((uint32_t)&_start);
/*
* Enable NAND clock
*/
/* Clear bypass bit */
writel(CLKCTRL_CLKSEQ_BYPASS_GPMI,
&clkctrl_regs->hw_clkctrl_clkseq_set);
/* Set GPMI clock to ref_gpmi / 12 */
clrsetbits_le32(&clkctrl_regs->hw_clkctrl_gpmi,
CLKCTRL_GPMI_CLKGATE | CLKCTRL_GPMI_DIV_MASK, 1);
udelay(1000);
/*
* Configure GPIO unit
*/
mxs_gpio_init();
#ifdef CONFIG_APBH_DMA
/* Start APBH DMA */
mxs_dma_init();
#endif
return 0;
}
u32 get_cpu_rev(void)
{
struct mxs_digctl_regs *digctl_regs =
(struct mxs_digctl_regs *)MXS_DIGCTL_BASE;
uint8_t rev = readl(&digctl_regs->hw_digctl_chipid) & 0x000000FF;
switch (readl(&digctl_regs->hw_digctl_chipid) & HW_DIGCTL_CHIPID_MASK) {
case HW_DIGCTL_CHIPID_MX23:
switch (rev) {
case 0x0:
case 0x1:
case 0x2:
case 0x3:
case 0x4:
return (MXC_CPU_MX23 << 12) | (rev + 0x10);
default:
return 0;
}
case HW_DIGCTL_CHIPID_MX28:
switch (rev) {
case 0x1:
return (MXC_CPU_MX28 << 12) | 0x12;
default:
return 0;
}
default:
return 0;
}
}
#if defined(CONFIG_DISPLAY_CPUINFO)
const char *get_imx_type(u32 imxtype)
{
switch (imxtype) {
case MXC_CPU_MX23:
return "23";
case MXC_CPU_MX28:
return "28";
default:
return "??";
}
}
int print_cpuinfo(void)
{
u32 cpurev;
struct mxs_spl_data *data = MXS_SPL_DATA;
cpurev = get_cpu_rev();
printf("CPU: Freescale i.MX%s rev%d.%d at %d MHz\n",
get_imx_type((cpurev & 0xFF000) >> 12),
(cpurev & 0x000F0) >> 4,
(cpurev & 0x0000F) >> 0,
mxc_get_clock(MXC_ARM_CLK) / 1000000);
printf("BOOT: %s\n", mxs_boot_modes[data->boot_mode_idx].mode);
return 0;
}
#endif
int do_mx28_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
printf("CPU: %3d MHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000);
printf("BUS: %3d MHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000000);
printf("EMI: %3d MHz\n", mxc_get_clock(MXC_EMI_CLK));
printf("GPMI: %3d MHz\n", mxc_get_clock(MXC_GPMI_CLK) / 1000000);
return 0;
}
/*
* Initializes on-chip ethernet controllers.
*/
#if defined(CONFIG_MX28) && defined(CONFIG_CMD_NET)
int cpu_eth_init(bd_t *bis)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
/* Turn on ENET clocks */
clrbits_le32(&clkctrl_regs->hw_clkctrl_enet,
CLKCTRL_ENET_SLEEP | CLKCTRL_ENET_DISABLE);
/* Set up ENET PLL for 50 MHz */
/* Power on ENET PLL */
writel(CLKCTRL_PLL2CTRL0_POWER,
&clkctrl_regs->hw_clkctrl_pll2ctrl0_set);
udelay(10);
/* Gate on ENET PLL */
writel(CLKCTRL_PLL2CTRL0_CLKGATE,
&clkctrl_regs->hw_clkctrl_pll2ctrl0_clr);
/* Enable pad output */
setbits_le32(&clkctrl_regs->hw_clkctrl_enet, CLKCTRL_ENET_CLK_OUT_EN);
return 0;
}
#endif
__weak void mx28_adjust_mac(int dev_id, unsigned char *mac)
{
mac[0] = 0x00;
mac[1] = 0x04; /* Use FSL vendor MAC address by default */
if (dev_id == 1) /* Let MAC1 be MAC0 + 1 by default */
mac[5] += 1;
}
#ifdef CONFIG_MX28_FEC_MAC_IN_OCOTP
#define MXS_OCOTP_MAX_TIMEOUT 1000000
void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
{
struct mxs_ocotp_regs *ocotp_regs =
(struct mxs_ocotp_regs *)MXS_OCOTP_BASE;
uint32_t data;
memset(mac, 0, 6);
writel(OCOTP_CTRL_RD_BANK_OPEN, &ocotp_regs->hw_ocotp_ctrl_set);
if (mxs_wait_mask_clr(&ocotp_regs->hw_ocotp_ctrl_reg, OCOTP_CTRL_BUSY,
MXS_OCOTP_MAX_TIMEOUT)) {
printf("MXS FEC: Can't get MAC from OCOTP\n");
return;
}
data = readl(&ocotp_regs->hw_ocotp_cust0);
mac[2] = (data >> 24) & 0xff;
mac[3] = (data >> 16) & 0xff;
mac[4] = (data >> 8) & 0xff;
mac[5] = data & 0xff;
mx28_adjust_mac(dev_id, mac);
}
#else
void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
{
memset(mac, 0, 6);
}
#endif
int mxs_dram_init(void)
{
struct mxs_spl_data *data = MXS_SPL_DATA;
if (data->mem_dram_size == 0) {
printf("MXS:\n"
"Error, the RAM size passed up from SPL is 0!\n");
hang();
}
gd->ram_size = data->mem_dram_size;
return 0;
}
U_BOOT_CMD(
clocks, CONFIG_SYS_MAXARGS, 1, do_mx28_showclocks,
"display clocks",
""
);
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Freescale i.MX28 SPL functions
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*/
#ifndef __M28_INIT_H__
#define __M28_INIT_H__
void early_delay(int delay);
void mxs_power_init(void);
#ifdef CONFIG_SPL_MXS_PSWITCH_WAIT
void mxs_power_wait_pswitch(void);
#else
static inline void mxs_power_wait_pswitch(void) { }
#endif
void mxs_mem_init(void);
uint32_t mxs_mem_get_size(void);
void mxs_lradc_init(void);
void mxs_lradc_enable_batt_measurement(void);
#endif /* __M28_INIT_H__ */
@@ -0,0 +1,11 @@
DISPLAYPROGRESS
SECTION 0x0 BOOTABLE
TAG LAST
LOAD 0x1000 spl/u-boot-spl.bin
LOAD 0x8000 spl/u-boot-spl.ivt
LOAD 0x8040 spl/u-boot-spl.sig
CALL HAB 0x8000 0x0
LOAD 0x40002000 u-boot.bin
LOAD 0x40001000 u-boot.ivt
LOAD 0x40001040 u-boot.sig
CALL HAB 0x40001000 0x0
@@ -0,0 +1,5 @@
DISPLAYPROGRESS
SECTION 0x0 BOOTABLE
TAG LAST
LOAD 0x1000 spl/u-boot-spl.bin
CALL 0x1000 0x0
@@ -0,0 +1,6 @@
DISPLAYPROGRESS
SECTION 0x0 BOOTABLE
TAG LAST
LOAD 0x1000 spl/u-boot-spl.bin
LOAD IVT 0x8000 0x1000
CALL HAB 0x8000 0x0
@@ -0,0 +1,7 @@
DISPLAYPROGRESS
SECTION 0x0 BOOTABLE
TAG LAST
LOAD 0x1000 spl/u-boot-spl.bin
CALL 0x1000 0x0
LOAD 0x40002000 u-boot.bin
CALL 0x40002000 0x0
@@ -0,0 +1,9 @@
DISPLAYPROGRESS
SECTION 0x0 BOOTABLE
TAG LAST
LOAD 0x1000 spl/u-boot-spl.bin
LOAD IVT 0x8000 0x1000
CALL HAB 0x8000 0x0
LOAD 0x40002000 u-boot.bin
LOAD IVT 0x8000 0x40002000
CALL HAB 0x8000 0x0
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX28 Boot setup
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*/
#include <common.h>
#include <config.h>
#include <serial.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <asm/gpio.h>
#include <linux/compiler.h>
#include "mxs_init.h"
DECLARE_GLOBAL_DATA_PTR;
static gd_t gdata __section(".data");
#ifdef CONFIG_SPL_SERIAL_SUPPORT
static bd_t bdata __section(".data");
#endif
/*
* This delay function is intended to be used only in early stage of boot, where
* clock are not set up yet. The timer used here is reset on every boot and
* takes a few seconds to roll. The boot doesn't take that long, so to keep the
* code simple, it doesn't take rolling into consideration.
*/
void early_delay(int delay)
{
struct mxs_digctl_regs *digctl_regs =
(struct mxs_digctl_regs *)MXS_DIGCTL_BASE;
uint32_t st = readl(&digctl_regs->hw_digctl_microseconds);
st += delay;
while (st > readl(&digctl_regs->hw_digctl_microseconds))
;
}
#if defined(CONFIG_MX23)
#define MUX_CONFIG_BOOTMODE_PAD (MXS_PAD_3V3 | MXS_PAD_4MA | MXS_PAD_NOPULL)
static const iomux_cfg_t iomux_boot[] = {
MX23_PAD_LCD_D00__GPIO_1_0 | MUX_CONFIG_BOOTMODE_PAD,
MX23_PAD_LCD_D01__GPIO_1_1 | MUX_CONFIG_BOOTMODE_PAD,
MX23_PAD_LCD_D02__GPIO_1_2 | MUX_CONFIG_BOOTMODE_PAD,
MX23_PAD_LCD_D03__GPIO_1_3 | MUX_CONFIG_BOOTMODE_PAD,
MX23_PAD_LCD_D04__GPIO_1_4 | MUX_CONFIG_BOOTMODE_PAD,
MX23_PAD_LCD_D05__GPIO_1_5 | MUX_CONFIG_BOOTMODE_PAD,
};
#endif
static uint8_t mxs_get_bootmode_index(void)
{
uint8_t bootmode = 0;
int i;
uint8_t masked;
#if defined(CONFIG_MX23)
/* Setup IOMUX of bootmode pads to GPIO */
mxs_iomux_setup_multiple_pads(iomux_boot, ARRAY_SIZE(iomux_boot));
/* Setup bootmode pins as GPIO input */
gpio_direction_input(MX23_PAD_LCD_D00__GPIO_1_0);
gpio_direction_input(MX23_PAD_LCD_D01__GPIO_1_1);
gpio_direction_input(MX23_PAD_LCD_D02__GPIO_1_2);
gpio_direction_input(MX23_PAD_LCD_D03__GPIO_1_3);
gpio_direction_input(MX23_PAD_LCD_D05__GPIO_1_5);
/* Read bootmode pads */
bootmode |= (gpio_get_value(MX23_PAD_LCD_D00__GPIO_1_0) ? 1 : 0) << 0;
bootmode |= (gpio_get_value(MX23_PAD_LCD_D01__GPIO_1_1) ? 1 : 0) << 1;
bootmode |= (gpio_get_value(MX23_PAD_LCD_D02__GPIO_1_2) ? 1 : 0) << 2;
bootmode |= (gpio_get_value(MX23_PAD_LCD_D03__GPIO_1_3) ? 1 : 0) << 3;
bootmode |= (gpio_get_value(MX23_PAD_LCD_D05__GPIO_1_5) ? 1 : 0) << 5;
#elif defined(CONFIG_MX28)
/* The global boot mode will be detected by ROM code and its value
* is stored at the fixed address 0x00019BF0 in OCRAM.
*/
#define GLOBAL_BOOT_MODE_ADDR 0x00019BF0
bootmode = __raw_readl(GLOBAL_BOOT_MODE_ADDR);
#endif
for (i = 0; i < ARRAY_SIZE(mxs_boot_modes); i++) {
masked = bootmode & mxs_boot_modes[i].boot_mask;
if (masked == mxs_boot_modes[i].boot_pads)
break;
}
return i;
}
static void mxs_spl_fixup_vectors(void)
{
/*
* Copy our vector table to 0x0, since due to HAB, we cannot
* be loaded to 0x0. We want to have working vectoring though,
* thus this fixup. Our vectoring table is PIC, so copying is
* fine.
*/
extern uint32_t _start;
/* cppcheck-suppress nullPointer */
memcpy(0x0, &_start, 0x60);
}
static void mxs_spl_console_init(void)
{
#ifdef CONFIG_SPL_SERIAL_SUPPORT
gd->bd = &bdata;
gd->baudrate = CONFIG_BAUDRATE;
serial_init();
gd->have_console = 1;
#endif
}
void mxs_common_spl_init(const uint32_t arg, const uint32_t *resptr,
const iomux_cfg_t *iomux_setup,
const unsigned int iomux_size)
{
struct mxs_spl_data *data = MXS_SPL_DATA;
uint8_t bootmode = mxs_get_bootmode_index();
gd = &gdata;
mxs_spl_fixup_vectors();
mxs_iomux_setup_multiple_pads(iomux_setup, iomux_size);
mxs_spl_console_init();
debug("SPL: Serial Console Initialised\n");
mxs_power_init();
mxs_mem_init();
data->mem_dram_size = mxs_mem_get_size();
data->boot_mode_idx = bootmode;
mxs_power_wait_pswitch();
if (mxs_boot_modes[data->boot_mode_idx].boot_pads == MXS_BM_JTAG) {
debug("SPL: Waiting for JTAG user\n");
asm volatile ("x: b x");
}
}
#ifndef CONFIG_SPL_FRAMEWORK
/* Support aparatus */
inline void board_init_f(unsigned long bootflag)
{
for (;;)
;
}
inline void board_init_r(gd_t *id, ulong dest_addr)
{
for (;;)
;
}
#endif
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX28 Battery measurement init
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*/
#include <common.h>
#include <config.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include "mxs_init.h"
void mxs_lradc_init(void)
{
struct mxs_lradc_regs *regs = (struct mxs_lradc_regs *)MXS_LRADC_BASE;
debug("SPL: Initialisating LRADC\n");
writel(LRADC_CTRL0_SFTRST, &regs->hw_lradc_ctrl0_clr);
writel(LRADC_CTRL0_CLKGATE, &regs->hw_lradc_ctrl0_clr);
writel(LRADC_CTRL0_ONCHIP_GROUNDREF, &regs->hw_lradc_ctrl0_clr);
clrsetbits_le32(&regs->hw_lradc_ctrl3,
LRADC_CTRL3_CYCLE_TIME_MASK,
LRADC_CTRL3_CYCLE_TIME_6MHZ);
clrsetbits_le32(&regs->hw_lradc_ctrl4,
LRADC_CTRL4_LRADC7SELECT_MASK |
LRADC_CTRL4_LRADC6SELECT_MASK,
LRADC_CTRL4_LRADC7SELECT_CHANNEL7 |
LRADC_CTRL4_LRADC6SELECT_CHANNEL10);
}
void mxs_lradc_enable_batt_measurement(void)
{
struct mxs_lradc_regs *regs = (struct mxs_lradc_regs *)MXS_LRADC_BASE;
debug("SPL: Enabling LRADC battery measurement\n");
/* Check if the channel is present at all. */
if (!(readl(&regs->hw_lradc_status) & LRADC_STATUS_CHANNEL7_PRESENT)) {
debug("SPL: LRADC channel 7 is not present - aborting\n");
return;
}
debug("SPL: LRADC channel 7 is present - configuring\n");
writel(LRADC_CTRL1_LRADC7_IRQ_EN, &regs->hw_lradc_ctrl1_clr);
writel(LRADC_CTRL1_LRADC7_IRQ, &regs->hw_lradc_ctrl1_clr);
clrsetbits_le32(&regs->hw_lradc_conversion,
LRADC_CONVERSION_SCALE_FACTOR_MASK,
LRADC_CONVERSION_SCALE_FACTOR_LI_ION);
writel(LRADC_CONVERSION_AUTOMATIC, &regs->hw_lradc_conversion_set);
/* Configure the channel. */
writel((1 << 7) << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
&regs->hw_lradc_ctrl2_clr);
writel(0xffffffff, &regs->hw_lradc_ch7_clr);
clrbits_le32(&regs->hw_lradc_ch7, LRADC_CH_NUM_SAMPLES_MASK);
writel(LRADC_CH_ACCUMULATE, &regs->hw_lradc_ch7_clr);
/* Schedule the channel. */
writel(1 << 7, &regs->hw_lradc_ctrl0_set);
/* Start the channel sampling. */
writel(((1 << 7) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) |
((1 << 3) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) |
100, &regs->hw_lradc_delay3);
writel(0xffffffff, &regs->hw_lradc_ch7_clr);
writel(LRADC_DELAY_KICK, &regs->hw_lradc_delay3_set);
debug("SPL: LRADC channel 7 configuration complete\n");
}
@@ -0,0 +1,359 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX28 RAM init
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*/
#include <common.h>
#include <config.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <linux/compiler.h>
#include "mxs_init.h"
__weak uint32_t mxs_dram_vals[] = {
/*
* i.MX28 DDR2 at 200MHz
*/
#if defined(CONFIG_MX28)
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000100, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00010101, 0x01010101,
0x000f0f01, 0x0f02020a, 0x00000000, 0x00010101,
0x00000100, 0x00000100, 0x00000000, 0x00000002,
0x01010000, 0x07080403, 0x06005003, 0x0a0000c8,
0x02009c40, 0x0002030c, 0x0036a609, 0x031a0612,
0x02030202, 0x00c8001c, 0x00000000, 0x00000000,
0x00012100, 0xffff0303, 0x00012100, 0xffff0303,
0x00012100, 0xffff0303, 0x00012100, 0xffff0303,
0x00000003, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000612, 0x01000F02,
0x06120612, 0x00000200, 0x00020007, 0xf4004a27,
0xf4004a27, 0xf4004a27, 0xf4004a27, 0x07000300,
0x07000300, 0x07400300, 0x07400300, 0x00000005,
0x00000000, 0x00000000, 0x01000000, 0x01020408,
0x08040201, 0x000f1133, 0x00000000, 0x00001f04,
0x00001f04, 0x00001f04, 0x00001f04, 0x00001f04,
0x00001f04, 0x00001f04, 0x00001f04, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00010000, 0x00030404,
0x00000003, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x01010000,
0x01000000, 0x03030000, 0x00010303, 0x01020202,
0x00000000, 0x02040303, 0x21002103, 0x00061200,
0x06120612, 0x04420442, 0x04420442, 0x00040004,
0x00040004, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0xffffffff
/*
* i.MX23 DDR at 133MHz
*/
#elif defined(CONFIG_MX23)
0x01010001, 0x00010100, 0x01000101, 0x00000001,
0x00000101, 0x00000000, 0x00010000, 0x01000001,
0x00000000, 0x00000001, 0x07000200, 0x00070202,
0x02020000, 0x04040a01, 0x00000201, 0x02040000,
0x02000000, 0x19000f08, 0x0d0d0000, 0x02021313,
0x02061521, 0x0000000a, 0x00080008, 0x00200020,
0x00200020, 0x00200020, 0x000003f7, 0x00000000,
0x00000000, 0x00000020, 0x00000020, 0x00c80000,
0x000a23cd, 0x000000c8, 0x00006665, 0x00000000,
0x00000101, 0x00040001, 0x00000000, 0x00000000,
0x00010000
#else
#error Unsupported memory initialization
#endif
};
__weak void mxs_adjust_memory_params(uint32_t *dram_vals)
{
debug("SPL: Using default SDRAM parameters\n");
}
#ifdef CONFIG_MX28
static void initialize_dram_values(void)
{
int i;
debug("SPL: Setting mx28 board specific SDRAM parameters\n");
mxs_adjust_memory_params(mxs_dram_vals);
debug("SPL: Applying SDRAM parameters\n");
for (i = 0; i < ARRAY_SIZE(mxs_dram_vals); i++)
writel(mxs_dram_vals[i], MXS_DRAM_BASE + (4 * i));
}
#else
static void initialize_dram_values(void)
{
int i;
debug("SPL: Setting mx23 board specific SDRAM parameters\n");
mxs_adjust_memory_params(mxs_dram_vals);
/*
* HW_DRAM_CTL27, HW_DRAM_CTL28 and HW_DRAM_CTL35 are not initialized as
* per FSL bootlets code.
*
* mx23 Reference Manual marks HW_DRAM_CTL27 and HW_DRAM_CTL28 as
* "reserved".
* HW_DRAM_CTL8 is setup as the last element.
* So skip the initialization of these HW_DRAM_CTL registers.
*/
debug("SPL: Applying SDRAM parameters\n");
for (i = 0; i < ARRAY_SIZE(mxs_dram_vals); i++) {
if (i == 8 || i == 27 || i == 28 || i == 35)
continue;
writel(mxs_dram_vals[i], MXS_DRAM_BASE + (4 * i));
}
/*
* Enable tRAS lockout in HW_DRAM_CTL08 ; it must be the last
* element to be set
*/
writel((1 << 24), MXS_DRAM_BASE + (4 * 8));
}
#endif
static void mxs_mem_init_clock(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
#if defined(CONFIG_MX23)
/* Fractional divider for ref_emi is 33 ; 480 * 18 / 33 = 266MHz */
const unsigned char divider = 33;
#elif defined(CONFIG_MX28)
/* Fractional divider for ref_emi is 21 ; 480 * 18 / 21 = 411MHz */
const unsigned char divider = 21;
#endif
debug("SPL: Initialising FRAC0\n");
/* Gate EMI clock */
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_EMI]);
/* Set fractional divider for ref_emi */
writeb(CLKCTRL_FRAC_CLKGATE | (divider & CLKCTRL_FRAC_FRAC_MASK),
&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]);
/* Ungate EMI clock */
writeb(CLKCTRL_FRAC_CLKGATE,
&clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_EMI]);
early_delay(11000);
/* Set EMI clock divider for EMI clock to 411 / 2 = 205MHz */
writel((2 << CLKCTRL_EMI_DIV_EMI_OFFSET) |
(1 << CLKCTRL_EMI_DIV_XTAL_OFFSET),
&clkctrl_regs->hw_clkctrl_emi);
/* Unbypass EMI */
writel(CLKCTRL_CLKSEQ_BYPASS_EMI,
&clkctrl_regs->hw_clkctrl_clkseq_clr);
early_delay(10000);
debug("SPL: FRAC0 Initialised\n");
}
static void mxs_mem_setup_cpu_and_hbus(void)
{
struct mxs_clkctrl_regs *clkctrl_regs =
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
debug("SPL: Setting CPU and HBUS clock frequencies\n");
/* Set fractional divider for ref_cpu to 480 * 18 / 19 = 454MHz
* and ungate CPU clock */
writeb(19 & CLKCTRL_FRAC_FRAC_MASK,
(uint8_t *)&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
/* Set CPU bypass */
writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
&clkctrl_regs->hw_clkctrl_clkseq_set);
/* HBUS = 151MHz */
writel(CLKCTRL_HBUS_DIV_MASK, &clkctrl_regs->hw_clkctrl_hbus_set);
writel(((~3) << CLKCTRL_HBUS_DIV_OFFSET) & CLKCTRL_HBUS_DIV_MASK,
&clkctrl_regs->hw_clkctrl_hbus_clr);
early_delay(10000);
/* CPU clock divider = 1 */
clrsetbits_le32(&clkctrl_regs->hw_clkctrl_cpu,
CLKCTRL_CPU_DIV_CPU_MASK, 1);
/* Disable CPU bypass */
writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
&clkctrl_regs->hw_clkctrl_clkseq_clr);
early_delay(15000);
}
static void mxs_mem_setup_vdda(void)
{
struct mxs_power_regs *power_regs =
(struct mxs_power_regs *)MXS_POWER_BASE;
debug("SPL: Configuring VDDA\n");
writel((0xc << POWER_VDDACTRL_TRG_OFFSET) |
(0x7 << POWER_VDDACTRL_BO_OFFSET_OFFSET) |
POWER_VDDACTRL_LINREG_OFFSET_1STEPS_BELOW,
&power_regs->hw_power_vddactrl);
}
uint32_t mxs_mem_get_size(void)
{
uint32_t sz, da;
uint32_t *vt = (uint32_t *)0x20;
/* The following is "subs pc, r14, #4", used as return from DABT. */
const uint32_t data_abort_memdetect_handler = 0xe25ef004;
/* Replace the DABT handler. */
da = vt[4];
vt[4] = data_abort_memdetect_handler;
sz = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE);
/* Restore the old DABT handler. */
vt[4] = da;
return sz;
}
#ifdef CONFIG_MX23
static void mx23_mem_setup_vddmem(void)
{
struct mxs_power_regs *power_regs =
(struct mxs_power_regs *)MXS_POWER_BASE;
debug("SPL: Setting mx23 VDDMEM\n");
/* We must wait before and after disabling the current limiter! */
early_delay(10000);
clrbits_le32(&power_regs->hw_power_vddmemctrl,
POWER_VDDMEMCTRL_ENABLE_ILIMIT);
early_delay(10000);
}
static void mx23_mem_init(void)
{
debug("SPL: Initialising mx23 SDRAM Controller\n");
/*
* Reset/ungate the EMI block. This is essential, otherwise the system
* suffers from memory instability. This thing is mx23 specific and is
* no longer present on mx28.
*/
mxs_reset_block((struct mxs_register_32 *)MXS_EMI_BASE);
mx23_mem_setup_vddmem();
/*
* Configure the DRAM registers
*/
/* Clear START and SREFRESH bit from DRAM_CTL8 */
clrbits_le32(MXS_DRAM_BASE + 0x20, (1 << 16) | (1 << 8));
initialize_dram_values();
/* Set START bit in DRAM_CTL8 */
setbits_le32(MXS_DRAM_BASE + 0x20, 1 << 16);
clrbits_le32(MXS_DRAM_BASE + 0x40, 1 << 17);
/* Wait for EMI_STAT bit DRAM_HALTED */
for (;;) {
if (!(readl(MXS_EMI_BASE + 0x10) & (1 << 1)))
break;
early_delay(1000);
}
/* Adjust EMI port priority. */
clrsetbits_le32(0x80020000, 0x1f << 16, 0x2);
early_delay(20000);
setbits_le32(MXS_DRAM_BASE + 0x40, 1 << 19);
setbits_le32(MXS_DRAM_BASE + 0x40, 1 << 11);
}
#endif
#ifdef CONFIG_MX28
static void mx28_mem_init(void)
{
struct mxs_pinctrl_regs *pinctrl_regs =
(struct mxs_pinctrl_regs *)MXS_PINCTRL_BASE;
debug("SPL: Initialising mx28 SDRAM Controller\n");
/* Set DDR2 mode */
writel(PINCTRL_EMI_DS_CTRL_DDR_MODE_DDR2,
&pinctrl_regs->hw_pinctrl_emi_ds_ctrl_set);
/*
* Configure the DRAM registers
*/
/* Clear START bit from DRAM_CTL16 */
clrbits_le32(MXS_DRAM_BASE + 0x40, 1);
initialize_dram_values();
/* Clear SREFRESH bit from DRAM_CTL17 */
clrbits_le32(MXS_DRAM_BASE + 0x44, 1);
/* Set START bit in DRAM_CTL16 */
setbits_le32(MXS_DRAM_BASE + 0x40, 1);
/* Wait for bit 20 (DRAM init complete) in DRAM_CTL58 */
while (!(readl(MXS_DRAM_BASE + 0xe8) & (1 << 20)))
;
}
#endif
void mxs_mem_init(void)
{
early_delay(11000);
mxs_mem_init_clock();
mxs_mem_setup_vdda();
#if defined(CONFIG_MX23)
mx23_mem_init();
#elif defined(CONFIG_MX28)
mx28_mem_init();
#endif
early_delay(10000);
mxs_mem_setup_cpu_and_hbus();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* armboot - Startup Code for ARM926EJS CPU-core
*
* Copyright (c) 2003 Texas Instruments
*
* ----- Adapted for OMAP1610 OMAP730 from ARM925t code ------
*
* Copyright (c) 2001 Marius Groger <mag@sysgo.de>
* Copyright (c) 2002 Alex Zupke <azu@sysgo.de>
* Copyright (c) 2002 Gary Jennejohn <garyj@denx.de>
* Copyright (c) 2003 Richard Woodruff <r-woodruff2@ti.com>
* Copyright (c) 2003 Kshitij <kshitij@ti.com>
* Copyright (c) 2010 Albert Aribaud <albert.u.boot@aribaud.net>
*
* Change to support call back into iMX28 bootrom
* Copyright (c) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*/
#include <asm-offsets.h>
#include <config.h>
#include <common.h>
/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
*
*************************************************************************
*/
.globl reset
reset:
/*
* If the CPU is configured in "Wait JTAG connection mode", the stack
* pointer is not configured and is zero. This will cause crash when
* trying to push data onto stack right below here. Load the SP and make
* it point to the end of OCRAM if the SP is zero.
*/
cmp sp, #0x00000000
ldreq sp, =CONFIG_SYS_INIT_SP_ADDR
/*
* Store all registers on old stack pointer, this will allow us later to
* return to the BootROM and let the BootROM load U-Boot into RAM.
*
* WARNING: Register r0 and r1 are used by the BootROM to pass data
* to the called code. Register r0 will contain arbitrary
* data that are set in the BootStream. In case this code
* was started with CALL instruction, register r1 will contain
* pointer to the return value this function can then set.
* The code below MUST NOT CHANGE register r0 and r1 !
*/
push {r0-r12,r14}
/* Save control register c1 */
mrc p15, 0, r2, c1, c0, 0
push {r2}
/* Set the cpu to SVC32 mode and store old CPSR register content. */
mrs r2, cpsr
push {r2}
bic r2, r2, #0x1f
orr r2, r2, #0xd3
msr cpsr, r2
bl board_init_ll
/* Restore BootROM's CPU mode (especially FIQ). */
pop {r2}
msr cpsr,r2
/*
* Restore c1 register. Especially set exception vector location
* back to BootROM space which is required by bootrom for USB boot.
*/
pop {r2}
mcr p15, 0, r2, c1, c0, 0
pop {r0-r12,r14}
/*
* In case this code was started by the CALL instruction, the register
* r0 is examined by the BootROM after this code returns. The value in
* r0 must be set to 0 to indicate successful return.
*/
mov r0, #0
bx lr
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX28 timer driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <time.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
/* Maximum fixed count */
#if defined(CONFIG_MX23)
#define TIMER_LOAD_VAL 0xffff
#elif defined(CONFIG_MX28)
#define TIMER_LOAD_VAL 0xffffffff
#endif
DECLARE_GLOBAL_DATA_PTR;
#define timestamp (gd->arch.tbl)
#define lastdec (gd->arch.lastinc)
/*
* This driver uses 1kHz clock source.
*/
#define MXS_INCREMENTER_HZ 1000
static inline unsigned long tick_to_time(unsigned long tick)
{
return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
}
static inline unsigned long time_to_tick(unsigned long time)
{
return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
}
/* Calculate how many ticks happen in "us" microseconds */
static inline unsigned long us_to_tick(unsigned long us)
{
return (us * MXS_INCREMENTER_HZ) / 1000000;
}
int timer_init(void)
{
struct mxs_timrot_regs *timrot_regs =
(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
/* Reset Timers and Rotary Encoder module */
mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
/* Set fixed_count to 0 */
#if defined(CONFIG_MX23)
writel(0, &timrot_regs->hw_timrot_timcount0);
#elif defined(CONFIG_MX28)
writel(0, &timrot_regs->hw_timrot_fixed_count0);
#endif
/* Set UPDATE bit and 1Khz frequency */
writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
&timrot_regs->hw_timrot_timctrl0);
/* Set fixed_count to maximal value */
#if defined(CONFIG_MX23)
writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
#elif defined(CONFIG_MX28)
writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
#endif
return 0;
}
unsigned long long get_ticks(void)
{
struct mxs_timrot_regs *timrot_regs =
(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
uint32_t now;
/* Current tick value */
#if defined(CONFIG_MX23)
/* Upper bits are the valid ones. */
now = readl(&timrot_regs->hw_timrot_timcount0) >>
TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
#elif defined(CONFIG_MX28)
now = readl(&timrot_regs->hw_timrot_running_count0);
#else
#error "Don't know how to read timrot_regs"
#endif
if (lastdec >= now) {
/*
* normal mode (non roll)
* move stamp forward with absolut diff ticks
*/
timestamp += (lastdec - now);
} else {
/* we have rollover of decrementer */
timestamp += (TIMER_LOAD_VAL - now) + lastdec;
}
lastdec = now;
return timestamp;
}
ulong get_timer(ulong base)
{
return tick_to_time(get_ticks()) - base;
}
/* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
#define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0
void __udelay(unsigned long usec)
{
uint32_t old, new, incr;
uint32_t counter = 0;
old = readl(MXS_HW_DIGCTL_MICROSECONDS);
while (counter < usec) {
new = readl(MXS_HW_DIGCTL_MICROSECONDS);
/* Check if the timer wrapped. */
if (new < old) {
incr = 0xffffffff - old;
incr += new;
} else {
incr = new - old;
}
/*
* Check if we are close to the maximum time and the counter
* would wrap if incremented. If that's the case, break out
* from the loop as the requested delay time passed.
*/
if (counter + incr < counter)
break;
counter += incr;
old = new;
}
}
ulong get_tbclk(void)
{
return MXS_INCREMENTER_HZ;
}
@@ -0,0 +1,18 @@
options {
driveTag = 0x00;
flags = 0x01;
}
sources {
u_boot_spl="spl/u-boot-spl.bin";
u_boot="u-boot.bin";
}
section (0) {
load u_boot_spl > 0x0000;
load ivt (entry = 0x0014) > 0x8000;
call 0x8000;
load u_boot > 0x40000100;
call 0x40000100;
}
@@ -0,0 +1,14 @@
sources {
u_boot_spl="spl/u-boot-spl.bin";
u_boot="u-boot.bin";
}
section (0) {
load u_boot_spl > 0x0000;
load ivt (entry = 0x0014) > 0x8000;
hab call 0x8000;
load u_boot > 0x40000100;
load ivt (entry = 0x40000100) > 0x8000;
hab call 0x8000;
}
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* January 2004 - Changed to support H4 device
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = IMAGE_TEXT_BASE;
. = ALIGN(4);
.text :
{
*(.vectors)
arch/arm/cpu/arm926ejs/mxs/start.o (.text*)
*(.text*)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*)
}
. = ALIGN(4);
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.bss : {
. = ALIGN(4);
__bss_start = .;
*(.bss*)
. = ALIGN(4);
__bss_end = .;
}
.end :
{
*(.__end)
}
_image_binary_end = .;
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.hash : { *(.hash*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
}