mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 16:39:35 +00:00
e7bed5c2b3
i2c didn't work on imx25 due to missing MXC_IPG_PERCLK. Now using MXC_I2C_CLK on all imx systems using i2c. Signed-off-by: Matthias Weisser <weisserm@arcor.de> Acked-by: Stefano Babic <sbabic@denx.de>
484 lines
11 KiB
C
484 lines
11 KiB
C
/*
|
|
* (C) Copyright 2007
|
|
* Sascha Hauer, Pengutronix
|
|
*
|
|
* (C) Copyright 2008-2010 Freescale Semiconductor, Inc.
|
|
*
|
|
* 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 <div64.h>
|
|
#include <asm/io.h>
|
|
#include <asm/errno.h>
|
|
#include <asm/arch/imx-regs.h>
|
|
#include <asm/arch/crm_regs.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/arch/sys_proto.h>
|
|
#ifdef CONFIG_FSL_ESDHC
|
|
#include <fsl_esdhc.h>
|
|
#endif
|
|
#include <netdev.h>
|
|
|
|
#define CLK_CODE(arm, ahb, sel) (((arm) << 16) + ((ahb) << 8) + (sel))
|
|
#define CLK_CODE_ARM(c) (((c) >> 16) & 0xFF)
|
|
#define CLK_CODE_AHB(c) (((c) >> 8) & 0xFF)
|
|
#define CLK_CODE_PATH(c) ((c) & 0xFF)
|
|
|
|
#define CCM_GET_DIVIDER(x, m, o) (((x) & (m)) >> (o))
|
|
|
|
#ifdef CONFIG_FSL_ESDHC
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
#endif
|
|
|
|
static int g_clk_mux_auto[8] = {
|
|
CLK_CODE(1, 3, 0), CLK_CODE(1, 2, 1), CLK_CODE(2, 1, 1), -1,
|
|
CLK_CODE(1, 6, 0), CLK_CODE(1, 4, 1), CLK_CODE(2, 2, 1), -1,
|
|
};
|
|
|
|
static int g_clk_mux_consumer[16] = {
|
|
CLK_CODE(1, 4, 0), CLK_CODE(1, 3, 1), CLK_CODE(1, 3, 1), -1,
|
|
-1, -1, CLK_CODE(4, 1, 0), CLK_CODE(1, 5, 0),
|
|
CLK_CODE(1, 8, 1), CLK_CODE(1, 6, 1), CLK_CODE(2, 4, 0), -1,
|
|
-1, -1, CLK_CODE(4, 2, 0), -1,
|
|
};
|
|
|
|
static int hsp_div_table[3][16] = {
|
|
{4, 3, 2, -1, -1, -1, 1, 5, 4, 3, 2, -1, -1, -1, 1, -1},
|
|
{-1, -1, -1, -1, -1, -1, -1, -1, 8, 6, 4, -1, -1, -1, 2, -1},
|
|
{3, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
};
|
|
|
|
u32 get_cpu_rev(void)
|
|
{
|
|
int reg;
|
|
struct iim_regs *iim =
|
|
(struct iim_regs *)IIM_BASE_ADDR;
|
|
reg = readl(&iim->iim_srev);
|
|
if (!reg) {
|
|
reg = readw(ROMPATCH_REV);
|
|
reg <<= 4;
|
|
} else {
|
|
reg += CHIP_REV_1_0;
|
|
}
|
|
|
|
return 0x35000 + (reg & 0xFF);
|
|
}
|
|
|
|
static u32 get_arm_div(u32 pdr0, u32 *fi, u32 *fd)
|
|
{
|
|
int *pclk_mux;
|
|
if (pdr0 & MXC_CCM_PDR0_AUTO_CON) {
|
|
pclk_mux = g_clk_mux_consumer +
|
|
((pdr0 & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >>
|
|
MXC_CCM_PDR0_CON_MUX_DIV_OFFSET);
|
|
} else {
|
|
pclk_mux = g_clk_mux_auto +
|
|
((pdr0 & MXC_CCM_PDR0_AUTO_MUX_DIV_MASK) >>
|
|
MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET);
|
|
}
|
|
|
|
if ((*pclk_mux) == -1)
|
|
return -1;
|
|
|
|
if (fi && fd) {
|
|
if (!CLK_CODE_PATH(*pclk_mux)) {
|
|
*fi = *fd = 1;
|
|
return CLK_CODE_ARM(*pclk_mux);
|
|
}
|
|
if (pdr0 & MXC_CCM_PDR0_AUTO_CON) {
|
|
*fi = 3;
|
|
*fd = 4;
|
|
} else {
|
|
*fi = 2;
|
|
*fd = 3;
|
|
}
|
|
}
|
|
return CLK_CODE_ARM(*pclk_mux);
|
|
}
|
|
|
|
static int get_ahb_div(u32 pdr0)
|
|
{
|
|
int *pclk_mux;
|
|
|
|
pclk_mux = g_clk_mux_consumer +
|
|
((pdr0 & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >>
|
|
MXC_CCM_PDR0_CON_MUX_DIV_OFFSET);
|
|
|
|
if ((*pclk_mux) == -1)
|
|
return -1;
|
|
|
|
return CLK_CODE_AHB(*pclk_mux);
|
|
}
|
|
|
|
static u32 decode_pll(u32 reg, u32 infreq)
|
|
{
|
|
u32 mfi = (reg >> 10) & 0xf;
|
|
s32 mfn = reg & 0x3ff;
|
|
u32 mfd = (reg >> 16) & 0x3ff;
|
|
u32 pd = (reg >> 26) & 0xf;
|
|
|
|
mfi = mfi <= 5 ? 5 : mfi;
|
|
mfn = mfn >= 512 ? mfn - 1024 : mfn;
|
|
mfd += 1;
|
|
pd += 1;
|
|
|
|
return lldiv(2 * (u64)infreq * (mfi * mfd + mfn),
|
|
mfd * pd);
|
|
}
|
|
|
|
static u32 get_mcu_main_clk(void)
|
|
{
|
|
u32 arm_div = 0, fi = 0, fd = 0;
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
arm_div = get_arm_div(readl(&ccm->pdr0), &fi, &fd);
|
|
fi *= decode_pll(readl(&ccm->mpctl), MXC_HCLK);
|
|
return fi / (arm_div * fd);
|
|
}
|
|
|
|
static u32 get_ipg_clk(void)
|
|
{
|
|
u32 freq = get_mcu_main_clk();
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
u32 pdr0 = readl(&ccm->pdr0);
|
|
|
|
return freq / (get_ahb_div(pdr0) * 2);
|
|
}
|
|
|
|
static u32 get_ipg_per_clk(void)
|
|
{
|
|
u32 freq = get_mcu_main_clk();
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
u32 pdr0 = readl(&ccm->pdr0);
|
|
u32 pdr4 = readl(&ccm->pdr4);
|
|
u32 div;
|
|
if (pdr0 & MXC_CCM_PDR0_PER_SEL) {
|
|
div = CCM_GET_DIVIDER(pdr4,
|
|
MXC_CCM_PDR4_PER0_PODF_MASK,
|
|
MXC_CCM_PDR4_PER0_PODF_OFFSET) + 1;
|
|
} else {
|
|
div = CCM_GET_DIVIDER(pdr0,
|
|
MXC_CCM_PDR0_PER_PODF_MASK,
|
|
MXC_CCM_PDR0_PER_PODF_OFFSET) + 1;
|
|
div *= get_ahb_div(pdr0);
|
|
}
|
|
return freq / div;
|
|
}
|
|
|
|
u32 imx_get_uartclk(void)
|
|
{
|
|
u32 freq;
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
u32 pdr4 = readl(&ccm->pdr4);
|
|
|
|
if (readl(&ccm->pdr3) & MXC_CCM_PDR3_UART_M_U)
|
|
freq = get_mcu_main_clk();
|
|
else
|
|
freq = decode_pll(readl(&ccm->ppctl), MXC_HCLK);
|
|
freq /= CCM_GET_DIVIDER(pdr4,
|
|
MXC_CCM_PDR4_UART_PODF_MASK,
|
|
MXC_CCM_PDR4_UART_PODF_OFFSET) + 1;
|
|
return freq;
|
|
}
|
|
|
|
unsigned int mxc_get_main_clock(enum mxc_main_clock clk)
|
|
{
|
|
u32 nfc_pdf, hsp_podf;
|
|
u32 pll, ret_val = 0, usb_podf;
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
|
|
u32 reg = readl(&ccm->pdr0);
|
|
u32 reg4 = readl(&ccm->pdr4);
|
|
|
|
reg |= 0x1;
|
|
|
|
switch (clk) {
|
|
case CPU_CLK:
|
|
ret_val = get_mcu_main_clk();
|
|
break;
|
|
case AHB_CLK:
|
|
ret_val = get_mcu_main_clk();
|
|
break;
|
|
case HSP_CLK:
|
|
if (reg & CLKMODE_CONSUMER) {
|
|
hsp_podf = (reg >> 20) & 0x3;
|
|
pll = get_mcu_main_clk();
|
|
hsp_podf = hsp_div_table[hsp_podf][(reg>>16)&0xF];
|
|
if (hsp_podf > 0) {
|
|
ret_val = pll / hsp_podf;
|
|
} else {
|
|
puts("mismatch HSP with ARM clock setting\n");
|
|
ret_val = 0;
|
|
}
|
|
} else {
|
|
ret_val = get_mcu_main_clk();
|
|
}
|
|
break;
|
|
case IPG_CLK:
|
|
ret_val = get_ipg_clk();
|
|
break;
|
|
case IPG_PER_CLK:
|
|
ret_val = get_ipg_per_clk();
|
|
break;
|
|
case NFC_CLK:
|
|
nfc_pdf = (reg4 >> 28) & 0xF;
|
|
pll = get_mcu_main_clk();
|
|
/* AHB/nfc_pdf */
|
|
ret_val = pll / (nfc_pdf + 1);
|
|
break;
|
|
case USB_CLK:
|
|
usb_podf = (reg4 >> 22) & 0x3F;
|
|
if (reg4 & 0x200)
|
|
pll = get_mcu_main_clk();
|
|
else
|
|
pll = decode_pll(readl(&ccm->ppctl), MXC_HCLK);
|
|
|
|
ret_val = pll / (usb_podf + 1);
|
|
break;
|
|
default:
|
|
printf("Unknown clock: %d\n", clk);
|
|
break;
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
unsigned int mxc_get_peri_clock(enum mxc_peri_clock clk)
|
|
{
|
|
u32 ret_val = 0, pdf, pre_pdf, clk_sel;
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
u32 mpdr2 = readl(&ccm->pdr2);
|
|
u32 mpdr3 = readl(&ccm->pdr3);
|
|
u32 mpdr4 = readl(&ccm->pdr4);
|
|
|
|
switch (clk) {
|
|
case UART1_BAUD:
|
|
case UART2_BAUD:
|
|
case UART3_BAUD:
|
|
clk_sel = mpdr3 & (1 << 14);
|
|
pdf = (mpdr4 >> 10) & 0x3F;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) / (pdf + 1);
|
|
break;
|
|
case SSI1_BAUD:
|
|
pre_pdf = (mpdr2 >> 24) & 0x7;
|
|
pdf = mpdr2 & 0x3F;
|
|
clk_sel = mpdr2 & (1 << 6);
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) /
|
|
((pre_pdf + 1) * (pdf + 1));
|
|
break;
|
|
case SSI2_BAUD:
|
|
pre_pdf = (mpdr2 >> 27) & 0x7;
|
|
pdf = (mpdr2 >> 8) & 0x3F;
|
|
clk_sel = mpdr2 & (1 << 6);
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) /
|
|
((pre_pdf + 1) * (pdf + 1));
|
|
break;
|
|
case CSI_BAUD:
|
|
clk_sel = mpdr2 & (1 << 7);
|
|
pdf = (mpdr2 >> 16) & 0x3F;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) / (pdf + 1);
|
|
break;
|
|
case MSHC_CLK:
|
|
pre_pdf = readl(&ccm->pdr1);
|
|
clk_sel = (pre_pdf & 0x80);
|
|
pdf = (pre_pdf >> 22) & 0x3F;
|
|
pre_pdf = (pre_pdf >> 28) & 0x7;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) /
|
|
((pre_pdf + 1) * (pdf + 1));
|
|
break;
|
|
case ESDHC1_CLK:
|
|
clk_sel = mpdr3 & 0x40;
|
|
pdf = mpdr3 & 0x3F;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) / (pdf + 1);
|
|
break;
|
|
case ESDHC2_CLK:
|
|
clk_sel = mpdr3 & 0x40;
|
|
pdf = (mpdr3 >> 8) & 0x3F;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) / (pdf + 1);
|
|
break;
|
|
case ESDHC3_CLK:
|
|
clk_sel = mpdr3 & 0x40;
|
|
pdf = (mpdr3 >> 16) & 0x3F;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) / (pdf + 1);
|
|
break;
|
|
case SPDIF_CLK:
|
|
clk_sel = mpdr3 & 0x400000;
|
|
pre_pdf = (mpdr3 >> 29) & 0x7;
|
|
pdf = (mpdr3 >> 23) & 0x3F;
|
|
ret_val = ((clk_sel != 0) ? mxc_get_main_clock(CPU_CLK) :
|
|
decode_pll(readl(&ccm->ppctl), MXC_HCLK)) /
|
|
((pre_pdf + 1) * (pdf + 1));
|
|
break;
|
|
default:
|
|
printf("%s(): This clock: %d not supported yet\n",
|
|
__func__, clk);
|
|
break;
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
unsigned int mxc_get_clock(enum mxc_clock clk)
|
|
{
|
|
switch (clk) {
|
|
case MXC_ARM_CLK:
|
|
return get_mcu_main_clk();
|
|
case MXC_AHB_CLK:
|
|
break;
|
|
case MXC_IPG_CLK:
|
|
return get_ipg_clk();
|
|
case MXC_IPG_PERCLK:
|
|
case MXC_I2C_CLK:
|
|
return get_ipg_per_clk();
|
|
case MXC_UART_CLK:
|
|
return imx_get_uartclk();
|
|
case MXC_ESDHC_CLK:
|
|
return mxc_get_peri_clock(ESDHC1_CLK);
|
|
case MXC_USB_CLK:
|
|
return mxc_get_main_clock(USB_CLK);
|
|
case MXC_FEC_CLK:
|
|
return get_ipg_clk();
|
|
case MXC_CSPI_CLK:
|
|
return get_ipg_clk();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#ifdef CONFIG_FEC_MXC
|
|
/*
|
|
* The MX35 has no fuse for MAC, return a NULL MAC
|
|
*/
|
|
void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
|
|
{
|
|
memset(mac, 0, 6);
|
|
}
|
|
|
|
u32 imx_get_fecclk(void)
|
|
{
|
|
return mxc_get_clock(MXC_IPG_CLK);
|
|
}
|
|
#endif
|
|
|
|
int do_mx35_showclocks(cmd_tbl_t *cmdtp,
|
|
int flag, int argc, char * const argv[])
|
|
{
|
|
u32 cpufreq = get_mcu_main_clk();
|
|
printf("mx35 cpu clock: %dMHz\n", cpufreq / 1000000);
|
|
printf("ipg clock : %dHz\n", get_ipg_clk());
|
|
printf("ipg per clock : %dHz\n", get_ipg_per_clk());
|
|
printf("uart clock : %dHz\n", mxc_get_clock(MXC_UART_CLK));
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
clocks, CONFIG_SYS_MAXARGS, 1, do_mx35_showclocks,
|
|
"display clocks",
|
|
""
|
|
);
|
|
|
|
#if defined(CONFIG_DISPLAY_CPUINFO)
|
|
static char *get_reset_cause(void)
|
|
{
|
|
/* read RCSR register from CCM module */
|
|
struct ccm_regs *ccm =
|
|
(struct ccm_regs *)IMX_CCM_BASE;
|
|
|
|
u32 cause = readl(&ccm->rcsr) & 0x0F;
|
|
|
|
switch (cause) {
|
|
case 0x0000:
|
|
return "POR";
|
|
case 0x0002:
|
|
return "JTAG";
|
|
case 0x0004:
|
|
return "RST";
|
|
case 0x0008:
|
|
return "WDOG";
|
|
default:
|
|
return "unknown reset";
|
|
}
|
|
}
|
|
|
|
int print_cpuinfo(void)
|
|
{
|
|
u32 srev = get_cpu_rev();
|
|
|
|
printf("CPU: Freescale i.MX35 rev %d.%d at %d MHz.\n",
|
|
(srev & 0xF0) >> 4, (srev & 0x0F),
|
|
get_mcu_main_clk() / 1000000);
|
|
|
|
printf("Reset cause: %s\n", get_reset_cause());
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Initializes on-chip ethernet controllers.
|
|
* to override, implement board_eth_init()
|
|
*/
|
|
int cpu_eth_init(bd_t *bis)
|
|
{
|
|
int rc = -ENODEV;
|
|
|
|
#if defined(CONFIG_FEC_MXC)
|
|
rc = fecmxc_initialize(bis);
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef CONFIG_FSL_ESDHC
|
|
/*
|
|
* Initializes on-chip MMC controllers.
|
|
* to override, implement board_mmc_init()
|
|
*/
|
|
int cpu_mmc_init(bd_t *bis)
|
|
{
|
|
return fsl_esdhc_mmc_init(bis);
|
|
}
|
|
#endif
|
|
|
|
int get_clocks(void)
|
|
{
|
|
#ifdef CONFIG_FSL_ESDHC
|
|
gd->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void reset_cpu(ulong addr)
|
|
{
|
|
struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
|
|
writew(4, &wdog->wcr);
|
|
}
|