mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-14 00:47:26 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
333 lines
11 KiB
C
333 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <asm/addrspace.h>
|
|
#include <asm/types.h>
|
|
#include <mach/ar71xx_regs.h>
|
|
#include <mach/ath79.h>
|
|
#include <wait_bit.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/*
|
|
* The math for calculating PLL:
|
|
* NFRAC * 2^8
|
|
* NINT + -------------
|
|
* XTAL [MHz] 2^(18 - 1)
|
|
* PLL [MHz] = ------------ * ----------------------
|
|
* REFDIV 2^OUTDIV
|
|
*
|
|
* Unfortunatelly, there is no way to reliably compute the variables.
|
|
* The vendor U-Boot port contains macros for various combinations of
|
|
* CPU PLL / DDR PLL / AHB bus speed and there is no obvious pattern
|
|
* in those numbers.
|
|
*/
|
|
struct ar934x_pll_config {
|
|
u8 range;
|
|
u8 refdiv;
|
|
u8 outdiv;
|
|
/* Index 0 is for XTAL=25MHz , Index 1 is for XTAL=40MHz */
|
|
u8 nint[2];
|
|
};
|
|
|
|
struct ar934x_clock_config {
|
|
u16 cpu_freq;
|
|
u16 ddr_freq;
|
|
u16 ahb_freq;
|
|
|
|
struct ar934x_pll_config cpu_pll;
|
|
struct ar934x_pll_config ddr_pll;
|
|
};
|
|
|
|
static const struct ar934x_clock_config ar934x_clock_config[] = {
|
|
{ 300, 300, 150, { 1, 1, 1, { 24, 15 } }, { 1, 1, 1, { 24, 15 } } },
|
|
{ 400, 200, 200, { 1, 1, 1, { 32, 20 } }, { 1, 1, 2, { 32, 20 } } },
|
|
{ 400, 400, 200, { 0, 1, 1, { 32, 20 } }, { 0, 1, 1, { 32, 20 } } },
|
|
{ 500, 400, 200, { 1, 1, 0, { 20, 12 } }, { 0, 1, 1, { 32, 20 } } },
|
|
{ 533, 400, 200, { 1, 1, 0, { 21, 13 } }, { 0, 1, 1, { 32, 20 } } },
|
|
{ 533, 500, 250, { 1, 1, 0, { 21, 13 } }, { 0, 1, 0, { 20, 12 } } },
|
|
{ 560, 480, 240, { 1, 1, 0, { 22, 14 } }, { 1, 1, 0, { 19, 12 } } },
|
|
{ 566, 400, 200, { 1, 1, 0, { 22, 14 } }, { 1, 1, 0, { 16, 10 } } },
|
|
{ 566, 450, 225, { 1, 1, 0, { 22, 14 } }, { 0, 1, 1, { 36, 22 } } },
|
|
{ 566, 475, 237, { 1, 1, 0, { 22, 14 } }, { 1, 1, 0, { 19, 11 } } },
|
|
{ 566, 500, 250, { 1, 1, 0, { 22, 14 } }, { 1, 1, 0, { 20, 12 } } },
|
|
{ 566, 525, 262, { 1, 1, 0, { 22, 14 } }, { 1, 1, 0, { 21, 13 } } },
|
|
{ 566, 550, 275, { 1, 1, 0, { 22, 14 } }, { 1, 1, 0, { 22, 13 } } },
|
|
{ 600, 266, 133, { 0, 1, 0, { 24, 15 } }, { 1, 1, 1, { 21, 16 } } },
|
|
{ 600, 266, 200, { 0, 1, 0, { 24, 15 } }, { 1, 1, 1, { 21, 16 } } },
|
|
{ 600, 300, 150, { 0, 1, 0, { 24, 15 } }, { 0, 1, 1, { 24, 15 } } },
|
|
{ 600, 332, 166, { 0, 1, 0, { 24, 15 } }, { 1, 1, 1, { 26, 16 } } },
|
|
{ 600, 332, 200, { 0, 1, 0, { 24, 15 } }, { 1, 1, 1, { 26, 16 } } },
|
|
{ 600, 400, 200, { 0, 1, 0, { 24, 15 } }, { 0, 1, 1, { 32, 20 } } },
|
|
{ 600, 450, 200, { 0, 1, 0, { 24, 15 } }, { 0, 1, 0, { 18, 20 } } },
|
|
{ 600, 500, 250, { 0, 1, 0, { 24, 15 } }, { 1, 1, 0, { 20, 12 } } },
|
|
{ 600, 525, 262, { 0, 1, 0, { 24, 15 } }, { 0, 1, 0, { 21, 20 } } },
|
|
{ 600, 550, 275, { 0, 1, 0, { 24, 15 } }, { 0, 1, 0, { 22, 20 } } },
|
|
{ 600, 575, 287, { 0, 1, 0, { 24, 15 } }, { 0, 1, 0, { 23, 14 } } },
|
|
{ 600, 600, 300, { 0, 1, 0, { 24, 15 } }, { 0, 1, 0, { 24, 20 } } },
|
|
{ 600, 650, 325, { 0, 1, 0, { 24, 15 } }, { 0, 1, 0, { 26, 20 } } },
|
|
{ 650, 600, 300, { 0, 1, 0, { 26, 15 } }, { 0, 1, 0, { 24, 20 } } },
|
|
{ 700, 400, 200, { 3, 1, 0, { 28, 17 } }, { 0, 1, 1, { 32, 20 } } },
|
|
};
|
|
|
|
static void ar934x_srif_pll_cfg(void __iomem *pll_reg_base, const u32 srif_val)
|
|
{
|
|
u32 reg;
|
|
do {
|
|
writel(0x10810f00, pll_reg_base + 0x4);
|
|
writel(srif_val, pll_reg_base + 0x0);
|
|
writel(0xd0810f00, pll_reg_base + 0x4);
|
|
writel(0x03000000, pll_reg_base + 0x8);
|
|
writel(0xd0800f00, pll_reg_base + 0x4);
|
|
|
|
clrbits_be32(pll_reg_base + 0x8, BIT(30));
|
|
udelay(5);
|
|
setbits_be32(pll_reg_base + 0x8, BIT(30));
|
|
udelay(5);
|
|
|
|
wait_for_bit_le32(pll_reg_base + 0xc, BIT(3), 1, 10, 0);
|
|
|
|
clrbits_be32(pll_reg_base + 0x8, BIT(30));
|
|
udelay(5);
|
|
|
|
/* Check if CPU SRIF PLL locked. */
|
|
reg = readl(pll_reg_base + 0x8);
|
|
reg = (reg & 0x7ffff8) >> 3;
|
|
} while (reg >= 0x40000);
|
|
}
|
|
|
|
void ar934x_pll_init(const u16 cpu_mhz, const u16 ddr_mhz, const u16 ahb_mhz)
|
|
{
|
|
void __iomem *srif_regs = map_physmem(AR934X_SRIF_BASE,
|
|
AR934X_SRIF_SIZE, MAP_NOCACHE);
|
|
void __iomem *pll_regs = map_physmem(AR71XX_PLL_BASE,
|
|
AR71XX_PLL_SIZE, MAP_NOCACHE);
|
|
const struct ar934x_pll_config *pll_cfg;
|
|
int i, pll_nint, pll_refdiv, xtal_40 = 0;
|
|
u32 reg, cpu_pll, cpu_srif, ddr_pll, ddr_srif;
|
|
|
|
/* Configure SRIF PLL with initial values. */
|
|
writel(0x13210f00, srif_regs + AR934X_SRIF_CPU_DPLL2_REG);
|
|
writel(0x03000000, srif_regs + AR934X_SRIF_CPU_DPLL3_REG);
|
|
writel(0x13210f00, srif_regs + AR934X_SRIF_DDR_DPLL2_REG);
|
|
writel(0x03000000, srif_regs + AR934X_SRIF_DDR_DPLL3_REG);
|
|
writel(0x03000000, srif_regs + 0x188); /* Undocumented reg :-) */
|
|
|
|
/* Test for 40MHz XTAL */
|
|
reg = ath79_get_bootstrap();
|
|
if (reg & AR934X_BOOTSTRAP_REF_CLK_40) {
|
|
xtal_40 = 1;
|
|
cpu_srif = 0x41c00000;
|
|
ddr_srif = 0x41680000;
|
|
} else {
|
|
xtal_40 = 0;
|
|
cpu_srif = 0x29c00000;
|
|
ddr_srif = 0x29680000;
|
|
}
|
|
|
|
/* Locate CPU/DDR PLL configuration */
|
|
for (i = 0; i < ARRAY_SIZE(ar934x_clock_config); i++) {
|
|
if (cpu_mhz != ar934x_clock_config[i].cpu_freq)
|
|
continue;
|
|
if (ddr_mhz != ar934x_clock_config[i].ddr_freq)
|
|
continue;
|
|
if (ahb_mhz != ar934x_clock_config[i].ahb_freq)
|
|
continue;
|
|
|
|
/* Entry found */
|
|
pll_cfg = &ar934x_clock_config[i].cpu_pll;
|
|
pll_nint = pll_cfg->nint[xtal_40];
|
|
pll_refdiv = pll_cfg->refdiv;
|
|
cpu_pll =
|
|
(pll_nint << AR934X_PLL_CPU_CONFIG_NINT_SHIFT) |
|
|
(pll_refdiv << AR934X_PLL_CPU_CONFIG_REFDIV_SHIFT) |
|
|
(pll_cfg->range << AR934X_PLL_CPU_CONFIG_RANGE_SHIFT) |
|
|
(pll_cfg->outdiv << AR934X_PLL_CPU_CONFIG_OUTDIV_SHIFT);
|
|
|
|
pll_cfg = &ar934x_clock_config[i].ddr_pll;
|
|
pll_nint = pll_cfg->nint[xtal_40];
|
|
pll_refdiv = pll_cfg->refdiv;
|
|
ddr_pll =
|
|
(pll_nint << AR934X_PLL_DDR_CONFIG_NINT_SHIFT) |
|
|
(pll_refdiv << AR934X_PLL_DDR_CONFIG_REFDIV_SHIFT) |
|
|
(pll_cfg->range << AR934X_PLL_DDR_CONFIG_RANGE_SHIFT) |
|
|
(pll_cfg->outdiv << AR934X_PLL_DDR_CONFIG_OUTDIV_SHIFT);
|
|
break;
|
|
}
|
|
|
|
/* PLL configuration not found, hang. */
|
|
if (i == ARRAY_SIZE(ar934x_clock_config))
|
|
hang();
|
|
|
|
/* Set PLL Bypass */
|
|
setbits_be32(pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG,
|
|
AR934X_PLL_CLK_CTRL_CPU_PLL_BYPASS);
|
|
setbits_be32(pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG,
|
|
AR934X_PLL_CLK_CTRL_DDR_PLL_BYPASS);
|
|
setbits_be32(pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG,
|
|
AR934X_PLL_CLK_CTRL_AHB_PLL_BYPASS);
|
|
|
|
/* Configure CPU PLL */
|
|
writel(cpu_pll | AR934X_PLL_CPU_CONFIG_PLLPWD,
|
|
pll_regs + AR934X_PLL_CPU_CONFIG_REG);
|
|
/* Configure DDR PLL */
|
|
writel(ddr_pll | AR934X_PLL_DDR_CONFIG_PLLPWD,
|
|
pll_regs + AR934X_PLL_DDR_CONFIG_REG);
|
|
/* Configure PLL routing */
|
|
writel(AR934X_PLL_CLK_CTRL_CPU_PLL_BYPASS |
|
|
AR934X_PLL_CLK_CTRL_DDR_PLL_BYPASS |
|
|
AR934X_PLL_CLK_CTRL_AHB_PLL_BYPASS |
|
|
(0 << AR934X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) |
|
|
(0 << AR934X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) |
|
|
(1 << AR934X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) |
|
|
AR934X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL |
|
|
AR934X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL |
|
|
AR934X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL,
|
|
pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG);
|
|
|
|
/* Configure SRIF PLLs, which is completely undocumented :-) */
|
|
ar934x_srif_pll_cfg(srif_regs + AR934X_SRIF_CPU_DPLL1_REG, cpu_srif);
|
|
ar934x_srif_pll_cfg(srif_regs + AR934X_SRIF_DDR_DPLL1_REG, ddr_srif);
|
|
|
|
/* Unset PLL Bypass */
|
|
clrbits_be32(pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG,
|
|
AR934X_PLL_CLK_CTRL_CPU_PLL_BYPASS);
|
|
clrbits_be32(pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG,
|
|
AR934X_PLL_CLK_CTRL_DDR_PLL_BYPASS);
|
|
clrbits_be32(pll_regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG,
|
|
AR934X_PLL_CLK_CTRL_AHB_PLL_BYPASS);
|
|
|
|
/* Enable PLL dithering */
|
|
writel((1 << AR934X_PLL_DDR_DIT_FRAC_STEP_SHIFT) |
|
|
(0xf << AR934X_PLL_DDR_DIT_UPD_CNT_SHIFT),
|
|
pll_regs + AR934X_PLL_DDR_DIT_FRAC_REG);
|
|
writel(48 << AR934X_PLL_CPU_DIT_UPD_CNT_SHIFT,
|
|
pll_regs + AR934X_PLL_CPU_DIT_FRAC_REG);
|
|
}
|
|
|
|
static u32 ar934x_get_xtal(void)
|
|
{
|
|
u32 val;
|
|
|
|
val = ath79_get_bootstrap();
|
|
if (val & AR934X_BOOTSTRAP_REF_CLK_40)
|
|
return 40000000;
|
|
else
|
|
return 25000000;
|
|
}
|
|
|
|
int get_serial_clock(void)
|
|
{
|
|
return ar934x_get_xtal();
|
|
}
|
|
|
|
static u32 ar934x_cpupll_to_hz(const u32 regval)
|
|
{
|
|
const u32 outdiv = (regval >> AR934X_PLL_CPU_CONFIG_OUTDIV_SHIFT) &
|
|
AR934X_PLL_CPU_CONFIG_OUTDIV_MASK;
|
|
const u32 refdiv = (regval >> AR934X_PLL_CPU_CONFIG_REFDIV_SHIFT) &
|
|
AR934X_PLL_CPU_CONFIG_REFDIV_MASK;
|
|
const u32 nint = (regval >> AR934X_PLL_CPU_CONFIG_NINT_SHIFT) &
|
|
AR934X_PLL_CPU_CONFIG_NINT_MASK;
|
|
const u32 nfrac = (regval >> AR934X_PLL_CPU_CONFIG_NFRAC_SHIFT) &
|
|
AR934X_PLL_CPU_CONFIG_NFRAC_MASK;
|
|
const u32 xtal = ar934x_get_xtal();
|
|
|
|
return (xtal * (nint + (nfrac >> 9))) / (refdiv * (1 << outdiv));
|
|
}
|
|
|
|
static u32 ar934x_ddrpll_to_hz(const u32 regval)
|
|
{
|
|
const u32 outdiv = (regval >> AR934X_PLL_DDR_CONFIG_OUTDIV_SHIFT) &
|
|
AR934X_PLL_DDR_CONFIG_OUTDIV_MASK;
|
|
const u32 refdiv = (regval >> AR934X_PLL_DDR_CONFIG_REFDIV_SHIFT) &
|
|
AR934X_PLL_DDR_CONFIG_REFDIV_MASK;
|
|
const u32 nint = (regval >> AR934X_PLL_DDR_CONFIG_NINT_SHIFT) &
|
|
AR934X_PLL_DDR_CONFIG_NINT_MASK;
|
|
const u32 nfrac = (regval >> AR934X_PLL_DDR_CONFIG_NFRAC_SHIFT) &
|
|
AR934X_PLL_DDR_CONFIG_NFRAC_MASK;
|
|
const u32 xtal = ar934x_get_xtal();
|
|
|
|
return (xtal * (nint + (nfrac >> 9))) / (refdiv * (1 << outdiv));
|
|
}
|
|
|
|
static void ar934x_update_clock(void)
|
|
{
|
|
void __iomem *regs;
|
|
u32 ctrl, cpu, cpupll, ddr, ddrpll;
|
|
u32 cpudiv, ddrdiv, busdiv;
|
|
u32 cpuclk, ddrclk, busclk;
|
|
|
|
regs = map_physmem(AR71XX_PLL_BASE, AR71XX_PLL_SIZE,
|
|
MAP_NOCACHE);
|
|
|
|
cpu = readl(regs + AR934X_PLL_CPU_CONFIG_REG);
|
|
ddr = readl(regs + AR934X_PLL_DDR_CONFIG_REG);
|
|
ctrl = readl(regs + AR934X_PLL_CPU_DDR_CLK_CTRL_REG);
|
|
|
|
cpupll = ar934x_cpupll_to_hz(cpu);
|
|
ddrpll = ar934x_ddrpll_to_hz(ddr);
|
|
|
|
if (ctrl & AR934X_PLL_CLK_CTRL_CPU_PLL_BYPASS)
|
|
cpuclk = ar934x_get_xtal();
|
|
else if (ctrl & AR934X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL)
|
|
cpuclk = cpupll;
|
|
else
|
|
cpuclk = ddrpll;
|
|
|
|
if (ctrl & AR934X_PLL_CLK_CTRL_DDR_PLL_BYPASS)
|
|
ddrclk = ar934x_get_xtal();
|
|
else if (ctrl & AR934X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL)
|
|
ddrclk = ddrpll;
|
|
else
|
|
ddrclk = cpupll;
|
|
|
|
if (ctrl & AR934X_PLL_CLK_CTRL_AHB_PLL_BYPASS)
|
|
busclk = ar934x_get_xtal();
|
|
else if (ctrl & AR934X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL)
|
|
busclk = ddrpll;
|
|
else
|
|
busclk = cpupll;
|
|
|
|
cpudiv = (ctrl >> AR934X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) &
|
|
AR934X_PLL_CLK_CTRL_CPU_POST_DIV_MASK;
|
|
ddrdiv = (ctrl >> AR934X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) &
|
|
AR934X_PLL_CLK_CTRL_DDR_POST_DIV_MASK;
|
|
busdiv = (ctrl >> AR934X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) &
|
|
AR934X_PLL_CLK_CTRL_AHB_POST_DIV_MASK;
|
|
|
|
gd->cpu_clk = cpuclk / (cpudiv + 1);
|
|
gd->mem_clk = ddrclk / (ddrdiv + 1);
|
|
gd->bus_clk = busclk / (busdiv + 1);
|
|
}
|
|
|
|
ulong get_bus_freq(ulong dummy)
|
|
{
|
|
ar934x_update_clock();
|
|
return gd->bus_clk;
|
|
}
|
|
|
|
ulong get_ddr_freq(ulong dummy)
|
|
{
|
|
ar934x_update_clock();
|
|
return gd->mem_clk;
|
|
}
|
|
|
|
int do_ar934x_showclk(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
ar934x_update_clock();
|
|
printf("CPU: %8ld MHz\n", gd->cpu_clk / 1000000);
|
|
printf("Memory: %8ld MHz\n", gd->mem_clk / 1000000);
|
|
printf("AHB: %8ld MHz\n", gd->bus_clk / 1000000);
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
clocks, CONFIG_SYS_MAXARGS, 1, do_ar934x_showclk,
|
|
"display clocks",
|
|
""
|
|
);
|