GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
if CPU_V7A
|
||||
|
||||
config CPU_V7_HAS_NONSEC
|
||||
bool
|
||||
|
||||
config CPU_V7_HAS_VIRT
|
||||
bool
|
||||
|
||||
config ARCH_SUPPORT_PSCI
|
||||
bool
|
||||
|
||||
config ARMV7_NONSEC
|
||||
bool "Enable support for booting in non-secure mode" if EXPERT
|
||||
depends on CPU_V7_HAS_NONSEC
|
||||
default y
|
||||
---help---
|
||||
Say Y here to enable support for booting in non-secure / SVC mode.
|
||||
|
||||
config ARMV7_BOOT_SEC_DEFAULT
|
||||
bool "Boot in secure mode by default" if EXPERT
|
||||
depends on ARMV7_NONSEC
|
||||
default y if TEGRA
|
||||
---help---
|
||||
Say Y here to boot in secure mode by default even if non-secure mode
|
||||
is supported. This option is useful to boot kernels which do not
|
||||
suppport booting in non-secure mode. Only set this if you need it.
|
||||
This can be overridden at run-time by setting the bootm_boot_mode env.
|
||||
variable to "sec" or "nonsec".
|
||||
|
||||
config ARMV7_VIRT
|
||||
bool "Enable support for hardware virtualization" if EXPERT
|
||||
depends on CPU_V7_HAS_VIRT && ARMV7_NONSEC
|
||||
default y
|
||||
---help---
|
||||
Say Y here to boot in hypervisor (HYP) mode when booting non-secure.
|
||||
|
||||
config ARMV7_PSCI
|
||||
bool "Enable PSCI support" if EXPERT
|
||||
depends on ARMV7_NONSEC && ARCH_SUPPORT_PSCI
|
||||
default y
|
||||
help
|
||||
Say Y here to enable PSCI support.
|
||||
|
||||
config ARMV7_PSCI_NR_CPUS
|
||||
int "Maximum supported CPUs for PSCI"
|
||||
depends on ARMV7_NONSEC
|
||||
default 4
|
||||
help
|
||||
The maximum number of CPUs supported in the PSCI firmware.
|
||||
It is no problem to set a larger value than the number of
|
||||
CPUs in the actual hardware implementation.
|
||||
|
||||
config ARMV7_LPAE
|
||||
bool "Use LPAE page table format" if EXPERT
|
||||
depends on CPU_V7A
|
||||
default y if ARMV7_VIRT
|
||||
---help---
|
||||
Say Y here to use the long descriptor page table format. This is
|
||||
required if U-Boot runs in HYP mode.
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2000-2003
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
extra-y := start.o
|
||||
|
||||
obj-y += cache_v7.o cache_v7_asm.o
|
||||
|
||||
obj-y += cpu.o cp15.o
|
||||
obj-y += syslib.o
|
||||
|
||||
obj-$(CONFIG_SYS_ARM_MPU) += mpu_v7r.o
|
||||
|
||||
ifneq ($(CONFIG_SPL_BUILD),y)
|
||||
obj-$(CONFIG_EFI_LOADER) += sctlr.o
|
||||
obj-$(CONFIG_ARMV7_NONSEC) += exception_level.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_SKIP_LOWLEVEL_INIT),y)
|
||||
obj-y += lowlevel_init.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_ARM_SMCCC) += smccc-call.o
|
||||
obj-$(CONFIG_ARMV7_NONSEC) += nonsec_virt.o virt-v7.o virt-dt.o
|
||||
obj-$(CONFIG_ARMV7_PSCI) += psci.o psci-common.o
|
||||
|
||||
obj-$(CONFIG_IPROC) += iproc-common/
|
||||
obj-$(CONFIG_KONA) += kona-common/
|
||||
obj-$(CONFIG_SYS_ARCH_TIMER) += arch_timer.o
|
||||
|
||||
ifneq (,$(filter s5pc1xx exynos,$(SOC)))
|
||||
obj-y += s5p-common/
|
||||
endif
|
||||
|
||||
obj-$(if $(filter bcm235xx,$(SOC)),y) += bcm235xx/
|
||||
obj-$(if $(filter bcm281xx,$(SOC)),y) += bcm281xx/
|
||||
obj-$(if $(filter bcmcygnus,$(SOC)),y) += bcmcygnus/
|
||||
obj-$(if $(filter bcmnsp,$(SOC)),y) += bcmnsp/
|
||||
obj-$(if $(filter ls102xa,$(SOC)),y) += ls102xa/
|
||||
obj-$(CONFIG_RMOBILE) += rmobile/
|
||||
obj-$(if $(filter stv0991,$(SOC)),y) += stv0991/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_VF610) += vf610/
|
||||
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2012-2014
|
||||
* Texas Instruments Incorporated, <www.ti.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <div64.h>
|
||||
#include <bootstage.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#ifndef CONFIG_SYS_HZ_CLOCK
|
||||
static inline u32 read_cntfrq(void)
|
||||
{
|
||||
u32 frq;
|
||||
|
||||
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (frq));
|
||||
return frq;
|
||||
}
|
||||
#endif
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
gd->arch.tbl = 0;
|
||||
gd->arch.tbu = 0;
|
||||
|
||||
#ifdef CONFIG_SYS_HZ_CLOCK
|
||||
gd->arch.timer_rate_hz = CONFIG_SYS_HZ_CLOCK;
|
||||
#else
|
||||
gd->arch.timer_rate_hz = read_cntfrq();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
ulong nowl, nowu;
|
||||
|
||||
asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (nowl), "=r" (nowu));
|
||||
|
||||
gd->arch.tbl = nowl;
|
||||
gd->arch.tbu = nowu;
|
||||
|
||||
return (((unsigned long long)gd->arch.tbu) << 32) | gd->arch.tbl;
|
||||
}
|
||||
|
||||
|
||||
ulong timer_get_boot_us(void)
|
||||
{
|
||||
if (!gd->arch.timer_rate_hz)
|
||||
timer_init();
|
||||
|
||||
return lldiv(get_ticks(), gd->arch.timer_rate_hz / 1000000);
|
||||
}
|
||||
|
||||
ulong get_tbclk(void)
|
||||
{
|
||||
return gd->arch.timer_rate_hz;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2013 Broadcom Corporation.
|
||||
|
||||
obj-y += clk-core.o
|
||||
obj-y += clk-bcm235xx.o
|
||||
obj-y += clk-sdio.o
|
||||
obj-y += clk-bsc.o
|
||||
obj-$(CONFIG_BCM_SF2_ETH) += clk-eth.o
|
||||
obj-y += clk-usb-otg.o
|
||||
@@ -0,0 +1,568 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* bcm235xx-specific clock tables
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
#define CLOCK_1K 1000
|
||||
#define CLOCK_1M (CLOCK_1K * 1000)
|
||||
|
||||
/* declare a reference clock */
|
||||
#define DECLARE_REF_CLK(clk_name, clk_parent, clk_rate, clk_div) \
|
||||
static struct refclk clk_name = { \
|
||||
.clk = { \
|
||||
.name = #clk_name, \
|
||||
.parent = clk_parent, \
|
||||
.rate = clk_rate, \
|
||||
.div = clk_div, \
|
||||
.ops = &ref_clk_ops, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Reference clocks
|
||||
*/
|
||||
|
||||
/* Declare a list of reference clocks */
|
||||
DECLARE_REF_CLK(ref_crystal, 0, 26 * CLOCK_1M, 1);
|
||||
DECLARE_REF_CLK(var_96m, 0, 96 * CLOCK_1M, 1);
|
||||
DECLARE_REF_CLK(ref_96m, 0, 96 * CLOCK_1M, 1);
|
||||
DECLARE_REF_CLK(ref_312m, 0, 312 * CLOCK_1M, 0);
|
||||
DECLARE_REF_CLK(ref_104m, &ref_312m.clk, 104 * CLOCK_1M, 3);
|
||||
DECLARE_REF_CLK(ref_52m, &ref_104m.clk, 52 * CLOCK_1M, 2);
|
||||
DECLARE_REF_CLK(ref_13m, &ref_52m.clk, 13 * CLOCK_1M, 4);
|
||||
DECLARE_REF_CLK(var_312m, 0, 312 * CLOCK_1M, 0);
|
||||
DECLARE_REF_CLK(var_104m, &var_312m.clk, 104 * CLOCK_1M, 3);
|
||||
DECLARE_REF_CLK(var_52m, &var_104m.clk, 52 * CLOCK_1M, 2);
|
||||
DECLARE_REF_CLK(var_13m, &var_52m.clk, 13 * CLOCK_1M, 4);
|
||||
|
||||
struct refclk_lkup {
|
||||
struct refclk *procclk;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/* Lookup table for string to clk tranlation */
|
||||
#define MKSTR(x) {&x, #x}
|
||||
static struct refclk_lkup refclk_str_tbl[] = {
|
||||
MKSTR(ref_crystal), MKSTR(var_96m), MKSTR(ref_96m),
|
||||
MKSTR(ref_312m), MKSTR(ref_104m), MKSTR(ref_52m),
|
||||
MKSTR(ref_13m), MKSTR(var_312m), MKSTR(var_104m),
|
||||
MKSTR(var_52m), MKSTR(var_13m),
|
||||
};
|
||||
|
||||
int refclk_entries = sizeof(refclk_str_tbl)/sizeof(refclk_str_tbl[0]);
|
||||
|
||||
/* convert ref clock string to clock structure pointer */
|
||||
struct refclk *refclk_str_to_clk(const char *name)
|
||||
{
|
||||
int i;
|
||||
struct refclk_lkup *tblp = refclk_str_tbl;
|
||||
for (i = 0; i < refclk_entries; i++, tblp++) {
|
||||
if (!(strcmp(name, tblp->name)))
|
||||
return tblp->procclk;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* frequency tables indexed by freq_id */
|
||||
unsigned long master_axi_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
208 * CLOCK_1M,
|
||||
312 * CLOCK_1M,
|
||||
312 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long master_ahb_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
78 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
156 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long slave_axi_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
78 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long slave_apb_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
26 * CLOCK_1M,
|
||||
39 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
78 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long esub_freq_tbl[8] = {
|
||||
78 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
208 * CLOCK_1M,
|
||||
208 * CLOCK_1M,
|
||||
208 * CLOCK_1M
|
||||
};
|
||||
|
||||
static struct bus_clk_data bsc1_apb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0458, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data bsc2_apb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x045c, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data bsc3_apb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0484, 16, 0, 1),
|
||||
};
|
||||
|
||||
/* * Master CCU clocks */
|
||||
static struct peri_clk_data sdio1_data = {
|
||||
.gate = HW_SW_GATE(0x0358, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a28, 0, 3),
|
||||
.div = DIVIDER(0x0a28, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 9),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio2_data = {
|
||||
.gate = HW_SW_GATE(0x035c, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a2c, 0, 3),
|
||||
.div = DIVIDER(0x0a2c, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 10),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio3_data = {
|
||||
.gate = HW_SW_GATE(0x0364, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a34, 0, 3),
|
||||
.div = DIVIDER(0x0a34, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 12),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio4_data = {
|
||||
.gate = HW_SW_GATE(0x0360, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a30, 0, 3),
|
||||
.div = DIVIDER(0x0a30, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 11),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio1_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x0358, 20, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio2_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x035c, 20, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio3_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x0364, 20, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio4_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x0360, 20, 4),
|
||||
};
|
||||
|
||||
static struct bus_clk_data usb_otg_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0348, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio1_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0358, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio2_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x035c, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio3_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0364, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio4_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0360, 16, 0, 1),
|
||||
};
|
||||
|
||||
/* * Slave CCU clocks */
|
||||
static struct peri_clk_data bsc1_data = {
|
||||
.gate = HW_SW_GATE(0x0458, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a64, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 23),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc2_data = {
|
||||
.gate = HW_SW_GATE(0x045c, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a68, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 24),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc3_data = {
|
||||
.gate = HW_SW_GATE(0x0484, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a84, 0, 3),
|
||||
.trig = TRIGGER(0x0b00, 2),
|
||||
};
|
||||
|
||||
/*
|
||||
* CCU clocks
|
||||
*/
|
||||
|
||||
static struct ccu_clock kpm_ccu_clk = {
|
||||
.clk = {
|
||||
.name = "kpm_ccu_clk",
|
||||
.ops = &ccu_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.num_policy_masks = 1,
|
||||
.policy_freq_offset = 0x00000008,
|
||||
.freq_bit_shift = 8,
|
||||
.policy_ctl_offset = 0x0000000c,
|
||||
.policy0_mask_offset = 0x00000010,
|
||||
.policy1_mask_offset = 0x00000014,
|
||||
.policy2_mask_offset = 0x00000018,
|
||||
.policy3_mask_offset = 0x0000001c,
|
||||
.lvm_en_offset = 0x00000034,
|
||||
.freq_id = 2,
|
||||
.freq_tbl = master_axi_freq_tbl,
|
||||
};
|
||||
|
||||
static struct ccu_clock kps_ccu_clk = {
|
||||
.clk = {
|
||||
.name = "kps_ccu_clk",
|
||||
.ops = &ccu_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.num_policy_masks = 1,
|
||||
.policy_freq_offset = 0x00000008,
|
||||
.freq_bit_shift = 8,
|
||||
.policy_ctl_offset = 0x0000000c,
|
||||
.policy0_mask_offset = 0x00000010,
|
||||
.policy1_mask_offset = 0x00000014,
|
||||
.policy2_mask_offset = 0x00000018,
|
||||
.policy3_mask_offset = 0x0000001c,
|
||||
.lvm_en_offset = 0x00000034,
|
||||
.freq_id = 2,
|
||||
.freq_tbl = slave_axi_freq_tbl,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BCM_SF2_ETH
|
||||
static struct ccu_clock esub_ccu_clk = {
|
||||
.clk = {
|
||||
.name = "esub_ccu_clk",
|
||||
.ops = &ccu_clk_ops,
|
||||
.ccu_clk_mgr_base = ESUB_CLK_BASE_ADDR,
|
||||
},
|
||||
.num_policy_masks = 1,
|
||||
.policy_freq_offset = 0x00000008,
|
||||
.freq_bit_shift = 8,
|
||||
.policy_ctl_offset = 0x0000000c,
|
||||
.policy0_mask_offset = 0x00000010,
|
||||
.policy1_mask_offset = 0x00000014,
|
||||
.policy2_mask_offset = 0x00000018,
|
||||
.policy3_mask_offset = 0x0000001c,
|
||||
.lvm_en_offset = 0x00000034,
|
||||
.freq_id = 2,
|
||||
.freq_tbl = esub_freq_tbl,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Bus clocks
|
||||
*/
|
||||
|
||||
/* KPM bus clocks */
|
||||
static struct bus_clock usb_otg_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "usb_otg_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &usb_otg_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio1_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio1_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio1_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio2_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio2_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio2_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio3_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio3_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio3_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio4_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio4_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio4_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock bsc1_apb_clk = {
|
||||
.clk = {
|
||||
.name = "bsc1_apb_clk",
|
||||
.parent = &kps_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = slave_apb_freq_tbl,
|
||||
.data = &bsc1_apb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock bsc2_apb_clk = {
|
||||
.clk = {
|
||||
.name = "bsc2_apb_clk",
|
||||
.parent = &kps_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = slave_apb_freq_tbl,
|
||||
.data = &bsc2_apb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock bsc3_apb_clk = {
|
||||
.clk = {
|
||||
.name = "bsc3_apb_clk",
|
||||
.parent = &kps_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = slave_apb_freq_tbl,
|
||||
.data = &bsc3_apb_data,
|
||||
};
|
||||
|
||||
/* KPM peripheral */
|
||||
static struct peri_clock sdio1_clk = {
|
||||
.clk = {
|
||||
.name = "sdio1_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio1_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio2_clk = {
|
||||
.clk = {
|
||||
.name = "sdio2_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio2_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio3_clk = {
|
||||
.clk = {
|
||||
.name = "sdio3_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio3_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio4_clk = {
|
||||
.clk = {
|
||||
.name = "sdio4_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio4_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio1_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio1_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio1_sleep_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio2_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio2_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio2_sleep_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio3_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio3_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio3_sleep_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio4_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio4_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio4_sleep_data,
|
||||
};
|
||||
|
||||
/* KPS peripheral clock */
|
||||
static struct peri_clock bsc1_clk = {
|
||||
.clk = {
|
||||
.name = "bsc1_clk",
|
||||
.parent = &ref_13m.clk,
|
||||
.rate = 13 * CLOCK_1M,
|
||||
.div = 1,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &bsc1_data,
|
||||
};
|
||||
|
||||
static struct peri_clock bsc2_clk = {
|
||||
.clk = {
|
||||
.name = "bsc2_clk",
|
||||
.parent = &ref_13m.clk,
|
||||
.rate = 13 * CLOCK_1M,
|
||||
.div = 1,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &bsc2_data,
|
||||
};
|
||||
|
||||
static struct peri_clock bsc3_clk = {
|
||||
.clk = {
|
||||
.name = "bsc3_clk",
|
||||
.parent = &ref_13m.clk,
|
||||
.rate = 13 * CLOCK_1M,
|
||||
.div = 1,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &bsc3_data,
|
||||
};
|
||||
|
||||
/* public table for registering clocks */
|
||||
struct clk_lookup arch_clk_tbl[] = {
|
||||
/* Peripheral clocks */
|
||||
CLK_LK(sdio1),
|
||||
CLK_LK(sdio2),
|
||||
CLK_LK(sdio3),
|
||||
CLK_LK(sdio4),
|
||||
CLK_LK(sdio1_sleep),
|
||||
CLK_LK(sdio2_sleep),
|
||||
CLK_LK(sdio3_sleep),
|
||||
CLK_LK(sdio4_sleep),
|
||||
CLK_LK(bsc1),
|
||||
CLK_LK(bsc2),
|
||||
CLK_LK(bsc3),
|
||||
/* Bus clocks */
|
||||
CLK_LK(usb_otg_ahb),
|
||||
CLK_LK(sdio1_ahb),
|
||||
CLK_LK(sdio2_ahb),
|
||||
CLK_LK(sdio3_ahb),
|
||||
CLK_LK(sdio4_ahb),
|
||||
CLK_LK(bsc1_apb),
|
||||
CLK_LK(bsc2_apb),
|
||||
CLK_LK(bsc3_apb),
|
||||
#ifdef CONFIG_BCM_SF2_ETH
|
||||
CLK_LK(esub_ccu),
|
||||
#endif
|
||||
};
|
||||
|
||||
/* public array size */
|
||||
unsigned int arch_clk_tbl_array_size = ARRAY_SIZE(arch_clk_tbl);
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
/* Enable appropriate clocks for a BSC/I2C port */
|
||||
int clk_bsc_enable(void *base)
|
||||
{
|
||||
int ret;
|
||||
char *bscstr, *apbstr;
|
||||
|
||||
switch ((u32) base) {
|
||||
case PMU_BSC_BASE_ADDR:
|
||||
/* PMU clock is always enabled */
|
||||
return 0;
|
||||
case BSC1_BASE_ADDR:
|
||||
bscstr = "bsc1_clk";
|
||||
apbstr = "bsc1_apb_clk";
|
||||
break;
|
||||
case BSC2_BASE_ADDR:
|
||||
bscstr = "bsc2_clk";
|
||||
apbstr = "bsc2_apb_clk";
|
||||
break;
|
||||
case BSC3_BASE_ADDR:
|
||||
bscstr = "bsc3_clk";
|
||||
apbstr = "bsc3_apb_clk";
|
||||
break;
|
||||
default:
|
||||
printf("%s: base 0x%p not found\n", __func__, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Note that the bus clock must be enabled first */
|
||||
|
||||
ret = clk_get_and_enable(apbstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_and_enable(bscstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* bcm235xx architecture clock framework
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <bitfield.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
#define CLK_WR_ACCESS_PASSWORD 0x00a5a501
|
||||
#define WR_ACCESS_OFFSET 0 /* common to all clock blocks */
|
||||
#define POLICY_CTL_GO 1 /* Load and refresh policy masks */
|
||||
#define POLICY_CTL_GO_ATL 4 /* Active Load */
|
||||
|
||||
/* Helper function */
|
||||
int clk_get_and_enable(char *clkstr)
|
||||
{
|
||||
int ret = 0;
|
||||
struct clk *c;
|
||||
|
||||
debug("%s: %s\n", __func__, clkstr);
|
||||
|
||||
c = clk_get(clkstr);
|
||||
if (c) {
|
||||
ret = clk_enable(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
printf("%s: Couldn't find %s\n", __func__, clkstr);
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll a register in a CCU's address space, returning when the
|
||||
* specified bit in that register's value is set (or clear). Delay
|
||||
* a microsecond after each read of the register. Returns true if
|
||||
* successful, or false if we gave up trying.
|
||||
*
|
||||
* Caller must ensure the CCU lock is held.
|
||||
*/
|
||||
#define CLK_GATE_DELAY_USEC 2000
|
||||
static inline int wait_bit(void *base, u32 offset, u32 bit, bool want)
|
||||
{
|
||||
unsigned int tries;
|
||||
u32 bit_mask = 1 << bit;
|
||||
|
||||
for (tries = 0; tries < CLK_GATE_DELAY_USEC; tries++) {
|
||||
u32 val;
|
||||
bool bit_val;
|
||||
|
||||
val = readl(base + offset);
|
||||
bit_val = (val & bit_mask) ? 1 : 0;
|
||||
if (bit_val == want)
|
||||
return 0; /* success */
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
debug("%s: timeout on addr 0x%p, waiting for bit %d to go to %d\n",
|
||||
__func__, base + offset, bit, want);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Enable a peripheral clock */
|
||||
static int peri_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
struct peri_clock *peri_clk = to_peri_clk(c);
|
||||
struct peri_clk_data *cd = peri_clk->data;
|
||||
struct bcm_clk_gate *gate = &cd->gate;
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
|
||||
clk_get_rate(c); /* Make sure rate and sel are filled in */
|
||||
|
||||
/* enable access */
|
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET);
|
||||
|
||||
if (enable) {
|
||||
debug("%s %s set rate %lu div %lu sel %d parent %lu\n",
|
||||
__func__, c->name, c->rate, c->div, c->sel,
|
||||
c->parent->rate);
|
||||
|
||||
/*
|
||||
* clkgate - only software controllable gates are
|
||||
* supported by u-boot which includes all clocks
|
||||
* that matter. This avoids bringing in a lot of extra
|
||||
* complexity as done in the kernel framework.
|
||||
*/
|
||||
if (gate_exists(gate)) {
|
||||
reg = readl(base + cd->gate.offset);
|
||||
reg |= (1 << cd->gate.en_bit);
|
||||
writel(reg, base + cd->gate.offset);
|
||||
}
|
||||
|
||||
/* div and pll select */
|
||||
if (divider_exists(&cd->div)) {
|
||||
reg = readl(base + cd->div.offset);
|
||||
bitfield_replace(reg, cd->div.shift, cd->div.width,
|
||||
c->div - 1);
|
||||
writel(reg, base + cd->div.offset);
|
||||
}
|
||||
|
||||
/* frequency selector */
|
||||
if (selector_exists(&cd->sel)) {
|
||||
reg = readl(base + cd->sel.offset);
|
||||
bitfield_replace(reg, cd->sel.shift, cd->sel.width,
|
||||
c->sel);
|
||||
writel(reg, base + cd->sel.offset);
|
||||
}
|
||||
|
||||
/* trigger */
|
||||
if (trigger_exists(&cd->trig)) {
|
||||
writel((1 << cd->trig.bit), base + cd->trig.offset);
|
||||
|
||||
/* wait for trigger status bit to go to 0 */
|
||||
ret = wait_bit(base, cd->trig.offset, cd->trig.bit, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait for running (status_bit = 1) */
|
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
debug("%s disable clock %s\n", __func__, c->name);
|
||||
|
||||
/* clkgate */
|
||||
reg = readl(base + cd->gate.offset);
|
||||
reg &= ~(1 << cd->gate.en_bit);
|
||||
writel(reg, base + cd->gate.offset);
|
||||
|
||||
/* wait for stop (status_bit = 0) */
|
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, 0);
|
||||
}
|
||||
|
||||
/* disable access */
|
||||
writel(0, base + WR_ACCESS_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the rate of a peripheral clock */
|
||||
static int peri_clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
unsigned long diff;
|
||||
unsigned long new_rate = 0, div = 1;
|
||||
struct peri_clock *peri_clk = to_peri_clk(c);
|
||||
struct peri_clk_data *cd = peri_clk->data;
|
||||
const char **clock;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
diff = rate;
|
||||
|
||||
i = 0;
|
||||
for (clock = cd->clocks; *clock; clock++, i++) {
|
||||
struct refclk *ref = refclk_str_to_clk(*clock);
|
||||
if (!ref) {
|
||||
printf("%s: Lookup of %s failed\n", __func__, *clock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* round to the new rate */
|
||||
div = ref->clk.rate / rate;
|
||||
if (div == 0)
|
||||
div = 1;
|
||||
|
||||
new_rate = ref->clk.rate / div;
|
||||
|
||||
/* get the min diff */
|
||||
if (abs(new_rate - rate) < diff) {
|
||||
diff = abs(new_rate - rate);
|
||||
c->sel = i;
|
||||
c->parent = &ref->clk;
|
||||
c->rate = new_rate;
|
||||
c->div = div;
|
||||
}
|
||||
}
|
||||
|
||||
debug("%s %s set rate %lu div %lu sel %d parent %lu\n", __func__,
|
||||
c->name, c->rate, c->div, c->sel, c->parent->rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the rate of a peripheral clock */
|
||||
static unsigned long peri_clk_get_rate(struct clk *c)
|
||||
{
|
||||
struct peri_clock *peri_clk = to_peri_clk(c);
|
||||
struct peri_clk_data *cd = peri_clk->data;
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
int div = 1;
|
||||
const char **clock;
|
||||
struct refclk *ref;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (selector_exists(&cd->sel)) {
|
||||
reg = readl(base + cd->sel.offset);
|
||||
c->sel = bitfield_extract(reg, cd->sel.shift, cd->sel.width);
|
||||
} else {
|
||||
/*
|
||||
* For peri clocks that don't have a selector, the single
|
||||
* reference clock will always exist at index 0.
|
||||
*/
|
||||
c->sel = 0;
|
||||
}
|
||||
|
||||
if (divider_exists(&cd->div)) {
|
||||
reg = readl(base + cd->div.offset);
|
||||
div = bitfield_extract(reg, cd->div.shift, cd->div.width);
|
||||
div += 1;
|
||||
}
|
||||
|
||||
clock = cd->clocks;
|
||||
ref = refclk_str_to_clk(clock[c->sel]);
|
||||
if (!ref) {
|
||||
printf("%s: Can't lookup %s\n", __func__, clock[c->sel]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->parent = &ref->clk;
|
||||
c->div = div;
|
||||
c->rate = c->parent->rate / c->div;
|
||||
debug("%s parent rate %lu div %d sel %d rate %lu\n", __func__,
|
||||
c->parent->rate, div, c->sel, c->rate);
|
||||
|
||||
return c->rate;
|
||||
}
|
||||
|
||||
/* Peripheral clock operations */
|
||||
struct clk_ops peri_clk_ops = {
|
||||
.enable = peri_clk_enable,
|
||||
.set_rate = peri_clk_set_rate,
|
||||
.get_rate = peri_clk_get_rate,
|
||||
};
|
||||
|
||||
/* Enable a CCU clock */
|
||||
static int ccu_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
struct ccu_clock *ccu_clk = to_ccu_clk(c);
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (!enable)
|
||||
return -EINVAL; /* CCU clock cannot shutdown */
|
||||
|
||||
/* enable access */
|
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET);
|
||||
|
||||
/* config enable for policy engine */
|
||||
writel(1, base + ccu_clk->lvm_en_offset);
|
||||
|
||||
/* wait for bit to go to 0 */
|
||||
ret = wait_bit(base, ccu_clk->lvm_en_offset, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* freq ID */
|
||||
if (!ccu_clk->freq_bit_shift)
|
||||
ccu_clk->freq_bit_shift = 8;
|
||||
|
||||
/* Set frequency id for each of the 4 policies */
|
||||
reg = ccu_clk->freq_id |
|
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift)) |
|
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift * 2)) |
|
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift * 3));
|
||||
writel(reg, base + ccu_clk->policy_freq_offset);
|
||||
|
||||
/* enable all clock mask */
|
||||
writel(0x7fffffff, base + ccu_clk->policy0_mask_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy1_mask_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy2_mask_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy3_mask_offset);
|
||||
|
||||
if (ccu_clk->num_policy_masks == 2) {
|
||||
writel(0x7fffffff, base + ccu_clk->policy0_mask2_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy1_mask2_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy2_mask2_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy3_mask2_offset);
|
||||
}
|
||||
|
||||
/* start policy engine */
|
||||
reg = readl(base + ccu_clk->policy_ctl_offset);
|
||||
reg |= (POLICY_CTL_GO + POLICY_CTL_GO_ATL);
|
||||
writel(reg, base + ccu_clk->policy_ctl_offset);
|
||||
|
||||
/* wait till started */
|
||||
ret = wait_bit(base, ccu_clk->policy_ctl_offset, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* disable access */
|
||||
writel(0, base + WR_ACCESS_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the CCU clock rate */
|
||||
static unsigned long ccu_clk_get_rate(struct clk *c)
|
||||
{
|
||||
struct ccu_clock *ccu_clk = to_ccu_clk(c);
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
c->rate = ccu_clk->freq_tbl[ccu_clk->freq_id];
|
||||
return c->rate;
|
||||
}
|
||||
|
||||
/* CCU clock operations */
|
||||
struct clk_ops ccu_clk_ops = {
|
||||
.enable = ccu_clk_enable,
|
||||
.get_rate = ccu_clk_get_rate,
|
||||
};
|
||||
|
||||
/* Enable a bus clock */
|
||||
static int bus_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
struct bus_clock *bus_clk = to_bus_clk(c);
|
||||
struct bus_clk_data *cd = bus_clk->data;
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
/* enable access */
|
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET);
|
||||
|
||||
/* enable gating */
|
||||
reg = readl(base + cd->gate.offset);
|
||||
if (!!(reg & (1 << cd->gate.status_bit)) == !!enable)
|
||||
debug("%s already %s\n", c->name,
|
||||
enable ? "enabled" : "disabled");
|
||||
else {
|
||||
int want = (enable) ? 1 : 0;
|
||||
reg |= (1 << cd->gate.hw_sw_sel_bit);
|
||||
|
||||
if (enable)
|
||||
reg |= (1 << cd->gate.en_bit);
|
||||
else
|
||||
reg &= ~(1 << cd->gate.en_bit);
|
||||
|
||||
writel(reg, base + cd->gate.offset);
|
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit,
|
||||
want);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable access */
|
||||
writel(0, base + WR_ACCESS_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the rate of a bus clock */
|
||||
static unsigned long bus_clk_get_rate(struct clk *c)
|
||||
{
|
||||
struct bus_clock *bus_clk = to_bus_clk(c);
|
||||
struct ccu_clock *ccu_clk;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
ccu_clk = to_ccu_clk(c->parent);
|
||||
|
||||
c->rate = bus_clk->freq_tbl[ccu_clk->freq_id];
|
||||
c->div = ccu_clk->freq_tbl[ccu_clk->freq_id] / c->rate;
|
||||
return c->rate;
|
||||
}
|
||||
|
||||
/* Bus clock operations */
|
||||
struct clk_ops bus_clk_ops = {
|
||||
.enable = bus_clk_enable,
|
||||
.get_rate = bus_clk_get_rate,
|
||||
};
|
||||
|
||||
/* Enable a reference clock */
|
||||
static int ref_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reference clock operations */
|
||||
struct clk_ops ref_clk_ops = {
|
||||
.enable = ref_clk_enable,
|
||||
};
|
||||
|
||||
/*
|
||||
* clk.h implementation follows
|
||||
*/
|
||||
|
||||
/* Initialize the clock framework */
|
||||
int clk_init(void)
|
||||
{
|
||||
debug("%s:\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get a clock handle, give a name string */
|
||||
struct clk *clk_get(const char *con_id)
|
||||
{
|
||||
int i;
|
||||
struct clk_lookup *clk_tblp;
|
||||
|
||||
debug("%s: %s\n", __func__, con_id);
|
||||
|
||||
clk_tblp = arch_clk_tbl;
|
||||
for (i = 0; i < arch_clk_tbl_array_size; i++, clk_tblp++) {
|
||||
if (clk_tblp->con_id) {
|
||||
if (!con_id || strcmp(clk_tblp->con_id, con_id))
|
||||
continue;
|
||||
return clk_tblp->clk;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Enable a clock */
|
||||
int clk_enable(struct clk *c)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (!c->ops || !c->ops->enable)
|
||||
return -1;
|
||||
|
||||
/* enable parent clock first */
|
||||
if (c->parent)
|
||||
ret = clk_enable(c->parent);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!c->use_cnt)
|
||||
ret = c->ops->enable(c, 1);
|
||||
c->use_cnt++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable a clock */
|
||||
void clk_disable(struct clk *c)
|
||||
{
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (!c->ops || !c->ops->enable)
|
||||
return;
|
||||
|
||||
if (c->use_cnt > 0) {
|
||||
c->use_cnt--;
|
||||
if (c->use_cnt == 0)
|
||||
c->ops->enable(c, 0);
|
||||
}
|
||||
|
||||
/* disable parent */
|
||||
if (c->parent)
|
||||
clk_disable(c->parent);
|
||||
}
|
||||
|
||||
/* Get the clock rate */
|
||||
unsigned long clk_get_rate(struct clk *c)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (!c || !c->ops || !c->ops->get_rate)
|
||||
return 0;
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
|
||||
rate = c->ops->get_rate(c);
|
||||
debug("%s: rate = %ld\n", __func__, rate);
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* Set the clock rate */
|
||||
int clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!c || !c->ops || !c->ops->set_rate)
|
||||
return -EINVAL;
|
||||
debug("%s: %s rate=%ld\n", __func__, c->name, rate);
|
||||
|
||||
if (c->use_cnt)
|
||||
return -EINVAL;
|
||||
|
||||
ret = c->ops->set_rate(c, rate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Not required for this arch */
|
||||
/*
|
||||
long clk_round_rate(struct clk *clk, unsigned long rate);
|
||||
int clk_set_parent(struct clk *clk, struct clk *parent);
|
||||
struct clk *clk_get_parent(struct clk *clk);
|
||||
*/
|
||||
@@ -0,0 +1,490 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#ifdef CONFIG_CLK_DEBUG
|
||||
#undef writel
|
||||
#undef readl
|
||||
static inline void writel(u32 val, void *addr)
|
||||
{
|
||||
printf("Write [0x%p] = 0x%08x\n", addr, val);
|
||||
*(u32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline u32 readl(void *addr)
|
||||
{
|
||||
u32 val = *(u32 *)addr;
|
||||
printf("Read [0x%p] = 0x%08x\n", addr, val);
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct clk;
|
||||
|
||||
struct clk_lookup {
|
||||
const char *dev_id;
|
||||
const char *con_id;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
extern struct clk_lookup arch_clk_tbl[];
|
||||
extern unsigned int arch_clk_tbl_array_size;
|
||||
|
||||
/**
|
||||
* struct clk_ops - standard clock operations
|
||||
* @enable: enable/disable clock, see clk_enable() and clk_disable()
|
||||
* @set_rate: set the clock rate, see clk_set_rate().
|
||||
* @get_rate: get the clock rate, see clk_get_rate().
|
||||
* @round_rate: round a given clock rate, see clk_round_rate().
|
||||
* @set_parent: set the clock's parent, see clk_set_parent().
|
||||
*
|
||||
* Group the common clock implementations together so that we
|
||||
* don't have to keep setting the same fiels again. We leave
|
||||
* enable in struct clk.
|
||||
*
|
||||
*/
|
||||
struct clk_ops {
|
||||
int (*enable)(struct clk *c, int enable);
|
||||
int (*set_rate)(struct clk *c, unsigned long rate);
|
||||
unsigned long (*get_rate)(struct clk *c);
|
||||
unsigned long (*round_rate)(struct clk *c, unsigned long rate);
|
||||
int (*set_parent)(struct clk *c, struct clk *parent);
|
||||
};
|
||||
|
||||
struct clk {
|
||||
struct clk *parent;
|
||||
const char *name;
|
||||
int use_cnt;
|
||||
unsigned long rate; /* in HZ */
|
||||
|
||||
/* programmable divider. 0 means fixed ratio to parent clock */
|
||||
unsigned long div;
|
||||
|
||||
struct clk_src *src;
|
||||
struct clk_ops *ops;
|
||||
|
||||
unsigned long ccu_clk_mgr_base;
|
||||
int sel;
|
||||
};
|
||||
|
||||
struct refclk *refclk_str_to_clk(const char *name);
|
||||
|
||||
/* The common clock framework uses u8 to represent a parent index */
|
||||
#define PARENT_COUNT_MAX ((u32)U8_MAX)
|
||||
|
||||
#define BAD_CLK_INDEX U8_MAX /* Can't ever be valid */
|
||||
#define BAD_CLK_NAME ((const char *)-1)
|
||||
|
||||
#define BAD_SCALED_DIV_VALUE U64_MAX
|
||||
|
||||
/*
|
||||
* Utility macros for object flag management. If possible, flags
|
||||
* should be defined such that 0 is the desired default value.
|
||||
*/
|
||||
#define FLAG(type, flag) BCM_CLK_ ## type ## _FLAGS_ ## flag
|
||||
#define FLAG_SET(obj, type, flag) ((obj)->flags |= FLAG(type, flag))
|
||||
#define FLAG_CLEAR(obj, type, flag) ((obj)->flags &= ~(FLAG(type, flag)))
|
||||
#define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag))
|
||||
#define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag)))
|
||||
|
||||
/* Clock field state tests */
|
||||
|
||||
#define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS)
|
||||
#define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED)
|
||||
#define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW)
|
||||
#define gate_is_sw_controllable(gate) FLAG_TEST(gate, GATE, SW)
|
||||
#define gate_is_sw_managed(gate) FLAG_TEST(gate, GATE, SW_MANAGED)
|
||||
#define gate_is_no_disable(gate) FLAG_TEST(gate, GATE, NO_DISABLE)
|
||||
|
||||
#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED)
|
||||
|
||||
#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
|
||||
#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
|
||||
#define divider_has_fraction(div) (!divider_is_fixed(div) && \
|
||||
(div)->frac_width > 0)
|
||||
|
||||
#define selector_exists(sel) ((sel)->width != 0)
|
||||
#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS)
|
||||
|
||||
/* Clock type, used to tell common block what it's part of */
|
||||
enum bcm_clk_type {
|
||||
bcm_clk_none, /* undefined clock type */
|
||||
bcm_clk_bus,
|
||||
bcm_clk_core,
|
||||
bcm_clk_peri
|
||||
};
|
||||
|
||||
/*
|
||||
* Gating control and status is managed by a 32-bit gate register.
|
||||
*
|
||||
* There are several types of gating available:
|
||||
* - (no gate)
|
||||
* A clock with no gate is assumed to be always enabled.
|
||||
* - hardware-only gating (auto-gating)
|
||||
* Enabling or disabling clocks with this type of gate is
|
||||
* managed automatically by the hardware. Such clocks can be
|
||||
* considered by the software to be enabled. The current status
|
||||
* of auto-gated clocks can be read from the gate status bit.
|
||||
* - software-only gating
|
||||
* Auto-gating is not available for this type of clock.
|
||||
* Instead, software manages whether it's enabled by setting or
|
||||
* clearing the enable bit. The current gate status of a gate
|
||||
* under software control can be read from the gate status bit.
|
||||
* To ensure a change to the gating status is complete, the
|
||||
* status bit can be polled to verify that the gate has entered
|
||||
* the desired state.
|
||||
* - selectable hardware or software gating
|
||||
* Gating for this type of clock can be configured to be either
|
||||
* under software or hardware control. Which type is in use is
|
||||
* determined by the hw_sw_sel bit of the gate register.
|
||||
*/
|
||||
struct bcm_clk_gate {
|
||||
u32 offset; /* gate register offset */
|
||||
u32 status_bit; /* 0: gate is disabled; 0: gatge is enabled */
|
||||
u32 en_bit; /* 0: disable; 1: enable */
|
||||
u32 hw_sw_sel_bit; /* 0: hardware gating; 1: software gating */
|
||||
u32 flags; /* BCM_CLK_GATE_FLAGS_* below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Gate flags:
|
||||
* HW means this gate can be auto-gated
|
||||
* SW means the state of this gate can be software controlled
|
||||
* NO_DISABLE means this gate is (only) enabled if under software control
|
||||
* SW_MANAGED means the status of this gate is under software control
|
||||
* ENABLED means this software-managed gate is *supposed* to be enabled
|
||||
*/
|
||||
#define BCM_CLK_GATE_FLAGS_EXISTS ((u32)1 << 0) /* Gate is valid */
|
||||
#define BCM_CLK_GATE_FLAGS_HW ((u32)1 << 1) /* Can auto-gate */
|
||||
#define BCM_CLK_GATE_FLAGS_SW ((u32)1 << 2) /* Software control */
|
||||
#define BCM_CLK_GATE_FLAGS_NO_DISABLE ((u32)1 << 3) /* HW or enabled */
|
||||
#define BCM_CLK_GATE_FLAGS_SW_MANAGED ((u32)1 << 4) /* SW now in control */
|
||||
#define BCM_CLK_GATE_FLAGS_ENABLED ((u32)1 << 5) /* If SW_MANAGED */
|
||||
|
||||
/*
|
||||
* Gate initialization macros.
|
||||
*
|
||||
* Any gate initially under software control will be enabled.
|
||||
*/
|
||||
|
||||
/* A hardware/software gate initially under software control */
|
||||
#define HW_SW_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, SW_MANAGED)|FLAG(GATE, ENABLED)| \
|
||||
FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A hardware/software gate initially under hardware control */
|
||||
#define HW_SW_GATE_AUTO(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A hardware-or-enabled gate (enabled if not under hardware control) */
|
||||
#define HW_ENABLE_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, NO_DISABLE)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A software-only gate */
|
||||
#define SW_ONLY_GATE(_offset, _status_bit, _en_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.flags = FLAG(GATE, SW)|FLAG(GATE, SW_MANAGED)| \
|
||||
FLAG(GATE, ENABLED)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A hardware-only gate */
|
||||
#define HW_ONLY_GATE(_offset, _status_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Each clock can have zero, one, or two dividers which change the
|
||||
* output rate of the clock. Each divider can be either fixed or
|
||||
* variable. If there are two dividers, they are the "pre-divider"
|
||||
* and the "regular" or "downstream" divider. If there is only one,
|
||||
* there is no pre-divider.
|
||||
*
|
||||
* A fixed divider is any non-zero (positive) value, and it
|
||||
* indicates how the input rate is affected by the divider.
|
||||
*
|
||||
* The value of a variable divider is maintained in a sub-field of a
|
||||
* 32-bit divider register. The position of the field in the
|
||||
* register is defined by its offset and width. The value recorded
|
||||
* in this field is always 1 less than the value it represents.
|
||||
*
|
||||
* In addition, a variable divider can indicate that some subset
|
||||
* of its bits represent a "fractional" part of the divider. Such
|
||||
* bits comprise the low-order portion of the divider field, and can
|
||||
* be viewed as representing the portion of the divider that lies to
|
||||
* the right of the decimal point. Most variable dividers have zero
|
||||
* fractional bits. Variable dividers with non-zero fraction width
|
||||
* still record a value 1 less than the value they represent; the
|
||||
* added 1 does *not* affect the low-order bit in this case, it
|
||||
* affects the bits above the fractional part only. (Often in this
|
||||
* code a divider field value is distinguished from the value it
|
||||
* represents by referring to the latter as a "divisor".)
|
||||
*
|
||||
* In order to avoid dealing with fractions, divider arithmetic is
|
||||
* performed using "scaled" values. A scaled value is one that's
|
||||
* been left-shifted by the fractional width of a divider. Dividing
|
||||
* a scaled value by a scaled divisor produces the desired quotient
|
||||
* without loss of precision and without any other special handling
|
||||
* for fractions.
|
||||
*
|
||||
* The recorded value of a variable divider can be modified. To
|
||||
* modify either divider (or both), a clock must be enabled (i.e.,
|
||||
* using its gate). In addition, a trigger register (described
|
||||
* below) must be used to commit the change, and polled to verify
|
||||
* the change is complete.
|
||||
*/
|
||||
struct bcm_clk_div {
|
||||
union {
|
||||
struct { /* variable divider */
|
||||
u32 offset; /* divider register offset */
|
||||
u32 shift; /* field shift */
|
||||
u32 width; /* field width */
|
||||
u32 frac_width; /* field fraction width */
|
||||
|
||||
u64 scaled_div; /* scaled divider value */
|
||||
};
|
||||
u32 fixed; /* non-zero fixed divider value */
|
||||
};
|
||||
u32 flags; /* BCM_CLK_DIV_FLAGS_* below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Divider flags:
|
||||
* EXISTS means this divider exists
|
||||
* FIXED means it is a fixed-rate divider
|
||||
*/
|
||||
#define BCM_CLK_DIV_FLAGS_EXISTS ((u32)1 << 0) /* Divider is valid */
|
||||
#define BCM_CLK_DIV_FLAGS_FIXED ((u32)1 << 1) /* Fixed-value */
|
||||
|
||||
/* Divider initialization macros */
|
||||
|
||||
/* A fixed (non-zero) divider */
|
||||
#define FIXED_DIVIDER(_value) \
|
||||
{ \
|
||||
.fixed = (_value), \
|
||||
.flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \
|
||||
}
|
||||
|
||||
/* A divider with an integral divisor */
|
||||
#define DIVIDER(_offset, _shift, _width) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.scaled_div = BAD_SCALED_DIV_VALUE, \
|
||||
.flags = FLAG(DIV, EXISTS), \
|
||||
}
|
||||
|
||||
/* A divider whose divisor has an integer and fractional part */
|
||||
#define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.frac_width = (_frac_width), \
|
||||
.scaled_div = BAD_SCALED_DIV_VALUE, \
|
||||
.flags = FLAG(DIV, EXISTS), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Clocks may have multiple "parent" clocks. If there is more than
|
||||
* one, a selector must be specified to define which of the parent
|
||||
* clocks is currently in use. The selected clock is indicated in a
|
||||
* sub-field of a 32-bit selector register. The range of
|
||||
* representable selector values typically exceeds the number of
|
||||
* available parent clocks. Occasionally the reset value of a
|
||||
* selector field is explicitly set to a (specific) value that does
|
||||
* not correspond to a defined input clock.
|
||||
*
|
||||
* We register all known parent clocks with the common clock code
|
||||
* using a packed array (i.e., no empty slots) of (parent) clock
|
||||
* names, and refer to them later using indexes into that array.
|
||||
* We maintain an array of selector values indexed by common clock
|
||||
* index values in order to map between these common clock indexes
|
||||
* and the selector values used by the hardware.
|
||||
*
|
||||
* Like dividers, a selector can be modified, but to do so a clock
|
||||
* must be enabled, and a trigger must be used to commit the change.
|
||||
*/
|
||||
struct bcm_clk_sel {
|
||||
u32 offset; /* selector register offset */
|
||||
u32 shift; /* field shift */
|
||||
u32 width; /* field width */
|
||||
|
||||
u32 parent_count; /* number of entries in parent_sel[] */
|
||||
u32 *parent_sel; /* array of parent selector values */
|
||||
u8 clk_index; /* current selected index in parent_sel[] */
|
||||
};
|
||||
|
||||
/* Selector initialization macro */
|
||||
#define SELECTOR(_offset, _shift, _width) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.clk_index = BAD_CLK_INDEX, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Making changes to a variable divider or a selector for a clock
|
||||
* requires the use of a trigger. A trigger is defined by a single
|
||||
* bit within a register. To signal a change, a 1 is written into
|
||||
* that bit. To determine when the change has been completed, that
|
||||
* trigger bit is polled; the read value will be 1 while the change
|
||||
* is in progress, and 0 when it is complete.
|
||||
*
|
||||
* Occasionally a clock will have more than one trigger. In this
|
||||
* case, the "pre-trigger" will be used when changing a clock's
|
||||
* selector and/or its pre-divider.
|
||||
*/
|
||||
struct bcm_clk_trig {
|
||||
u32 offset; /* trigger register offset */
|
||||
u32 bit; /* trigger bit */
|
||||
u32 flags; /* BCM_CLK_TRIG_FLAGS_* below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Trigger flags:
|
||||
* EXISTS means this trigger exists
|
||||
*/
|
||||
#define BCM_CLK_TRIG_FLAGS_EXISTS ((u32)1 << 0) /* Trigger is valid */
|
||||
|
||||
/* Trigger initialization macro */
|
||||
#define TRIGGER(_offset, _bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.bit = (_bit), \
|
||||
.flags = FLAG(TRIG, EXISTS), \
|
||||
}
|
||||
|
||||
struct bus_clk_data {
|
||||
struct bcm_clk_gate gate;
|
||||
};
|
||||
|
||||
struct core_clk_data {
|
||||
struct bcm_clk_gate gate;
|
||||
};
|
||||
|
||||
struct peri_clk_data {
|
||||
struct bcm_clk_gate gate;
|
||||
struct bcm_clk_trig pre_trig;
|
||||
struct bcm_clk_div pre_div;
|
||||
struct bcm_clk_trig trig;
|
||||
struct bcm_clk_div div;
|
||||
struct bcm_clk_sel sel;
|
||||
const char *clocks[]; /* must be last; use CLOCKS() to declare */
|
||||
};
|
||||
#define CLOCKS(...) { __VA_ARGS__, NULL, }
|
||||
#define NO_CLOCKS { NULL, } /* Must use of no parent clocks */
|
||||
|
||||
struct refclk {
|
||||
struct clk clk;
|
||||
};
|
||||
|
||||
struct peri_clock {
|
||||
struct clk clk;
|
||||
struct peri_clk_data *data;
|
||||
};
|
||||
|
||||
struct ccu_clock {
|
||||
struct clk clk;
|
||||
|
||||
int num_policy_masks;
|
||||
unsigned long policy_freq_offset;
|
||||
int freq_bit_shift; /* 8 for most CCUs */
|
||||
unsigned long policy_ctl_offset;
|
||||
unsigned long policy0_mask_offset;
|
||||
unsigned long policy1_mask_offset;
|
||||
unsigned long policy2_mask_offset;
|
||||
unsigned long policy3_mask_offset;
|
||||
unsigned long policy0_mask2_offset;
|
||||
unsigned long policy1_mask2_offset;
|
||||
unsigned long policy2_mask2_offset;
|
||||
unsigned long policy3_mask2_offset;
|
||||
unsigned long lvm_en_offset;
|
||||
|
||||
int freq_id;
|
||||
unsigned long *freq_tbl;
|
||||
};
|
||||
|
||||
struct bus_clock {
|
||||
struct clk clk;
|
||||
struct bus_clk_data *data;
|
||||
unsigned long *freq_tbl;
|
||||
};
|
||||
|
||||
struct ref_clock {
|
||||
struct clk clk;
|
||||
};
|
||||
|
||||
static inline int is_same_clock(struct clk *a, struct clk *b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
#define to_clk(p) (&((p)->clk))
|
||||
#define name_to_clk(name) (&((name##_clk).clk))
|
||||
/* declare a struct clk_lookup */
|
||||
#define CLK_LK(name) \
|
||||
{.con_id = __stringify(name##_clk), .clk = name_to_clk(name),}
|
||||
|
||||
static inline struct refclk *to_refclk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct refclk, clk);
|
||||
}
|
||||
|
||||
static inline struct peri_clock *to_peri_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct peri_clock, clk);
|
||||
}
|
||||
|
||||
static inline struct ccu_clock *to_ccu_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct ccu_clock, clk);
|
||||
}
|
||||
|
||||
static inline struct bus_clock *to_bus_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct bus_clock, clk);
|
||||
}
|
||||
|
||||
static inline struct ref_clock *to_ref_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct ref_clock, clk);
|
||||
}
|
||||
|
||||
extern struct clk_ops peri_clk_ops;
|
||||
extern struct clk_ops ccu_clk_ops;
|
||||
extern struct clk_ops bus_clk_ops;
|
||||
extern struct clk_ops ref_clk_ops;
|
||||
|
||||
int clk_get_and_enable(char *clkstr);
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
#define WR_ACCESS_ADDR ESUB_CLK_BASE_ADDR
|
||||
#define WR_ACCESS_PASSWORD 0xA5A500
|
||||
|
||||
#define PLLE_POST_RESETB_ADDR (ESUB_CLK_BASE_ADDR + 0x00000C00)
|
||||
|
||||
#define PLLE_RESETB_ADDR (ESUB_CLK_BASE_ADDR + 0x00000C58)
|
||||
#define PLLE_RESETB_I_PLL_RESETB_PLLE_MASK 0x00010000
|
||||
#define PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK 0x00000001
|
||||
|
||||
#define PLL_LOCK_ADDR (ESUB_CLK_BASE_ADDR + 0x00000C38)
|
||||
#define PLL_LOCK_PLL_LOCK_PLLE_MASK 0x00000001
|
||||
|
||||
#define ESW_SYS_DIV_ADDR (ESUB_CLK_BASE_ADDR + 0x00000A04)
|
||||
#define ESW_SYS_DIV_PLL_SELECT_MASK 0x00000300
|
||||
#define ESW_SYS_DIV_DIV_MASK 0x0000001C
|
||||
#define ESW_SYS_DIV_PLL_VAR_208M_CLK_SELECT 0x00000100
|
||||
#define ESW_SYS_DIV_DIV_SELECT 0x4
|
||||
#define ESW_SYS_DIV_TRIGGER_MASK 0x00000001
|
||||
|
||||
#define ESUB_AXI_DIV_DEBUG_ADDR (ESUB_CLK_BASE_ADDR + 0x00000E04)
|
||||
#define ESUB_AXI_DIV_DEBUG_PLL_SELECT_MASK 0x0000001C
|
||||
#define ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK 0x00000040
|
||||
#define ESUB_AXI_DIV_DEBUG_PLL_VAR_208M_CLK_SELECT 0x0
|
||||
#define ESUB_AXI_DIV_DEBUG_TRIGGER_MASK 0x00000001
|
||||
|
||||
#define PLL_MAX_RETRY 100
|
||||
|
||||
/* Enable appropriate clocks for Ethernet */
|
||||
int clk_eth_enable(void)
|
||||
{
|
||||
int rc = -1;
|
||||
int retry_count = 0;
|
||||
rc = clk_get_and_enable("esub_ccu_clk");
|
||||
|
||||
/* Enable Access to CCU registers */
|
||||
writel((1 | WR_ACCESS_PASSWORD), WR_ACCESS_ADDR);
|
||||
|
||||
writel(readl(PLLE_POST_RESETB_ADDR) &
|
||||
~PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK,
|
||||
PLLE_POST_RESETB_ADDR);
|
||||
|
||||
/* Take PLL out of reset and put into normal mode */
|
||||
writel(readl(PLLE_RESETB_ADDR) | PLLE_RESETB_I_PLL_RESETB_PLLE_MASK,
|
||||
PLLE_RESETB_ADDR);
|
||||
|
||||
/* Wait for PLL lock */
|
||||
rc = -1;
|
||||
while (retry_count < PLL_MAX_RETRY) {
|
||||
udelay(100);
|
||||
if (readl(PLL_LOCK_ADDR) & PLL_LOCK_PLL_LOCK_PLLE_MASK) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
printf("%s: ETH-PLL lock timeout, Ethernet is not enabled!\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
writel(readl(PLLE_POST_RESETB_ADDR) |
|
||||
PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK,
|
||||
PLLE_POST_RESETB_ADDR);
|
||||
|
||||
/* Switch esw_sys_clk to use 104MHz(208MHz/2) clock */
|
||||
writel((readl(ESW_SYS_DIV_ADDR) &
|
||||
~(ESW_SYS_DIV_PLL_SELECT_MASK | ESW_SYS_DIV_DIV_MASK)) |
|
||||
ESW_SYS_DIV_PLL_VAR_208M_CLK_SELECT | ESW_SYS_DIV_DIV_SELECT,
|
||||
ESW_SYS_DIV_ADDR);
|
||||
|
||||
writel(readl(ESW_SYS_DIV_ADDR) | ESW_SYS_DIV_TRIGGER_MASK,
|
||||
ESW_SYS_DIV_ADDR);
|
||||
|
||||
/* Wait for trigger complete */
|
||||
rc = -1;
|
||||
retry_count = 0;
|
||||
while (retry_count < PLL_MAX_RETRY) {
|
||||
udelay(100);
|
||||
if (!(readl(ESW_SYS_DIV_ADDR) & ESW_SYS_DIV_TRIGGER_MASK)) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
printf("%s: SYS CLK Trigger timeout, Ethernet is not enabled!\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* switch Esub AXI clock to 208MHz */
|
||||
writel((readl(ESUB_AXI_DIV_DEBUG_ADDR) &
|
||||
~(ESUB_AXI_DIV_DEBUG_PLL_SELECT_MASK |
|
||||
ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK |
|
||||
ESUB_AXI_DIV_DEBUG_TRIGGER_MASK)) |
|
||||
ESUB_AXI_DIV_DEBUG_PLL_VAR_208M_CLK_SELECT |
|
||||
ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK,
|
||||
ESUB_AXI_DIV_DEBUG_ADDR);
|
||||
|
||||
writel(readl(ESUB_AXI_DIV_DEBUG_ADDR) |
|
||||
ESUB_AXI_DIV_DEBUG_TRIGGER_MASK,
|
||||
ESUB_AXI_DIV_DEBUG_ADDR);
|
||||
|
||||
/* Wait for trigger complete */
|
||||
rc = -1;
|
||||
retry_count = 0;
|
||||
while (retry_count < PLL_MAX_RETRY) {
|
||||
udelay(100);
|
||||
if (!(readl(ESUB_AXI_DIV_DEBUG_ADDR) &
|
||||
ESUB_AXI_DIV_DEBUG_TRIGGER_MASK)) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
printf("%s: AXI CLK Trigger timeout, Ethernet is not enabled!\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Disable Access to CCU registers */
|
||||
writel(WR_ACCESS_PASSWORD, WR_ACCESS_ADDR);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
/* Enable appropriate clocks for an SDIO port */
|
||||
int clk_sdio_enable(void *base, u32 rate, u32 *actual_ratep)
|
||||
{
|
||||
int ret;
|
||||
struct clk *c;
|
||||
|
||||
char *clkstr;
|
||||
char *slpstr;
|
||||
char *ahbstr;
|
||||
|
||||
switch ((u32) base) {
|
||||
case CONFIG_SYS_SDIO_BASE0:
|
||||
clkstr = CONFIG_SYS_SDIO0 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO0 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO0 "_sleep_clk";
|
||||
break;
|
||||
case CONFIG_SYS_SDIO_BASE1:
|
||||
clkstr = CONFIG_SYS_SDIO1 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO1 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO1 "_sleep_clk";
|
||||
break;
|
||||
case CONFIG_SYS_SDIO_BASE2:
|
||||
clkstr = CONFIG_SYS_SDIO2 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO2 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO2 "_sleep_clk";
|
||||
break;
|
||||
case CONFIG_SYS_SDIO_BASE3:
|
||||
clkstr = CONFIG_SYS_SDIO3 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO3 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO3 "_sleep_clk";
|
||||
break;
|
||||
default:
|
||||
printf("%s: base 0x%p not found\n", __func__, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_get_and_enable(ahbstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_and_enable(slpstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c = clk_get(clkstr);
|
||||
if (c) {
|
||||
ret = clk_set_rate(c, rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
printf("%s: Couldn't find %s\n", __func__, clkstr);
|
||||
return -EINVAL;
|
||||
}
|
||||
*actual_ratep = rate;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
/* Enable appropriate clocks for the USB OTG port */
|
||||
int clk_usb_otg_enable(void *base)
|
||||
{
|
||||
char *ahbstr;
|
||||
|
||||
switch ((u32) base) {
|
||||
case HSOTG_BASE_ADDR:
|
||||
ahbstr = "usb_otg_ahb_clk";
|
||||
break;
|
||||
default:
|
||||
printf("%s: base 0x%p not found\n", __func__, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return clk_get_and_enable(ahbstr);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2013 Broadcom Corporation.
|
||||
|
||||
obj-y += reset.o
|
||||
obj-y += clk-core.o
|
||||
obj-y += clk-bcm281xx.o
|
||||
obj-y += clk-sdio.o
|
||||
obj-y += clk-bsc.o
|
||||
obj-$(CONFIG_BCM_SF2_ETH) += clk-eth.o
|
||||
obj-y += clk-usb-otg.o
|
||||
@@ -0,0 +1,572 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* bcm281xx-specific clock tables
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
#define CLOCK_1K 1000
|
||||
#define CLOCK_1M (CLOCK_1K * 1000)
|
||||
|
||||
/* declare a reference clock */
|
||||
#define DECLARE_REF_CLK(clk_name, clk_parent, clk_rate, clk_div) \
|
||||
static struct refclk clk_name = { \
|
||||
.clk = { \
|
||||
.name = #clk_name, \
|
||||
.parent = clk_parent, \
|
||||
.rate = clk_rate, \
|
||||
.div = clk_div, \
|
||||
.ops = &ref_clk_ops, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Reference clocks
|
||||
*/
|
||||
|
||||
/* Declare a list of reference clocks */
|
||||
DECLARE_REF_CLK(ref_crystal, 0, 26 * CLOCK_1M, 1);
|
||||
DECLARE_REF_CLK(var_96m, 0, 96 * CLOCK_1M, 1);
|
||||
DECLARE_REF_CLK(ref_96m, 0, 96 * CLOCK_1M, 1);
|
||||
DECLARE_REF_CLK(ref_312m, 0, 312 * CLOCK_1M, 0);
|
||||
DECLARE_REF_CLK(ref_104m, &ref_312m.clk, 104 * CLOCK_1M, 3);
|
||||
DECLARE_REF_CLK(ref_52m, &ref_104m.clk, 52 * CLOCK_1M, 2);
|
||||
DECLARE_REF_CLK(ref_13m, &ref_52m.clk, 13 * CLOCK_1M, 4);
|
||||
DECLARE_REF_CLK(var_312m, 0, 312 * CLOCK_1M, 0);
|
||||
DECLARE_REF_CLK(var_104m, &var_312m.clk, 104 * CLOCK_1M, 3);
|
||||
DECLARE_REF_CLK(var_52m, &var_104m.clk, 52 * CLOCK_1M, 2);
|
||||
DECLARE_REF_CLK(var_13m, &var_52m.clk, 13 * CLOCK_1M, 4);
|
||||
|
||||
struct refclk_lkup {
|
||||
struct refclk *procclk;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/* Lookup table for string to clk tranlation */
|
||||
#define MKSTR(x) {&x, #x}
|
||||
static struct refclk_lkup refclk_str_tbl[] = {
|
||||
MKSTR(ref_crystal), MKSTR(var_96m), MKSTR(ref_96m),
|
||||
MKSTR(ref_312m), MKSTR(ref_104m), MKSTR(ref_52m),
|
||||
MKSTR(ref_13m), MKSTR(var_312m), MKSTR(var_104m),
|
||||
MKSTR(var_52m), MKSTR(var_13m),
|
||||
};
|
||||
|
||||
int refclk_entries = sizeof(refclk_str_tbl)/sizeof(refclk_str_tbl[0]);
|
||||
|
||||
/* convert ref clock string to clock structure pointer */
|
||||
struct refclk *refclk_str_to_clk(const char *name)
|
||||
{
|
||||
int i;
|
||||
struct refclk_lkup *tblp = refclk_str_tbl;
|
||||
for (i = 0; i < refclk_entries; i++, tblp++) {
|
||||
if (!(strcmp(name, tblp->name)))
|
||||
return tblp->procclk;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* frequency tables indexed by freq_id */
|
||||
unsigned long master_axi_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
208 * CLOCK_1M,
|
||||
312 * CLOCK_1M,
|
||||
312 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long master_ahb_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
78 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
156 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long slave_axi_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
78 * CLOCK_1M,
|
||||
104 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long slave_apb_freq_tbl[8] = {
|
||||
26 * CLOCK_1M,
|
||||
26 * CLOCK_1M,
|
||||
39 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
52 * CLOCK_1M,
|
||||
78 * CLOCK_1M
|
||||
};
|
||||
|
||||
unsigned long esub_freq_tbl[8] = {
|
||||
78 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
156 * CLOCK_1M,
|
||||
208 * CLOCK_1M,
|
||||
208 * CLOCK_1M,
|
||||
208 * CLOCK_1M
|
||||
};
|
||||
|
||||
static struct bus_clk_data bsc1_apb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0458, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data bsc2_apb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x045c, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data bsc3_apb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0484, 16, 0, 1),
|
||||
};
|
||||
|
||||
/* * Master CCU clocks */
|
||||
static struct peri_clk_data sdio1_data = {
|
||||
.gate = HW_SW_GATE(0x0358, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a28, 0, 3),
|
||||
.div = DIVIDER(0x0a28, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 9),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio2_data = {
|
||||
.gate = HW_SW_GATE(0x035c, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a2c, 0, 3),
|
||||
.div = DIVIDER(0x0a2c, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 10),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio3_data = {
|
||||
.gate = HW_SW_GATE(0x0364, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a34, 0, 3),
|
||||
.div = DIVIDER(0x0a34, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 12),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio4_data = {
|
||||
.gate = HW_SW_GATE(0x0360, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a30, 0, 3),
|
||||
.div = DIVIDER(0x0a30, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 11),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio1_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x0358, 20, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio2_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x035c, 20, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio3_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x0364, 20, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio4_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"),
|
||||
.gate = SW_ONLY_GATE(0x0360, 20, 4),
|
||||
};
|
||||
|
||||
static struct bus_clk_data usb_otg_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0348, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio1_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0358, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio2_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x035c, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio3_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0364, 16, 0, 1),
|
||||
};
|
||||
|
||||
static struct bus_clk_data sdio4_ahb_data = {
|
||||
.gate = HW_SW_GATE_AUTO(0x0360, 16, 0, 1),
|
||||
};
|
||||
|
||||
/* * Slave CCU clocks */
|
||||
static struct peri_clk_data bsc1_data = {
|
||||
.gate = HW_SW_GATE(0x0458, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a64, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 23),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc2_data = {
|
||||
.gate = HW_SW_GATE(0x045c, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a68, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 24),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc3_data = {
|
||||
.gate = HW_SW_GATE(0x0484, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a84, 0, 3),
|
||||
.trig = TRIGGER(0x0b00, 2),
|
||||
};
|
||||
|
||||
/*
|
||||
* CCU clocks
|
||||
*/
|
||||
|
||||
static struct ccu_clock kpm_ccu_clk = {
|
||||
.clk = {
|
||||
.name = "kpm_ccu_clk",
|
||||
.ops = &ccu_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.num_policy_masks = 1,
|
||||
.policy_freq_offset = 0x00000008,
|
||||
.freq_bit_shift = 8,
|
||||
.policy_ctl_offset = 0x0000000c,
|
||||
.policy0_mask_offset = 0x00000010,
|
||||
.policy1_mask_offset = 0x00000014,
|
||||
.policy2_mask_offset = 0x00000018,
|
||||
.policy3_mask_offset = 0x0000001c,
|
||||
.lvm_en_offset = 0x00000034,
|
||||
.freq_id = 2,
|
||||
.freq_tbl = master_axi_freq_tbl,
|
||||
};
|
||||
|
||||
static struct ccu_clock kps_ccu_clk = {
|
||||
.clk = {
|
||||
.name = "kps_ccu_clk",
|
||||
.ops = &ccu_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.num_policy_masks = 2,
|
||||
.policy_freq_offset = 0x00000008,
|
||||
.freq_bit_shift = 8,
|
||||
.policy_ctl_offset = 0x0000000c,
|
||||
.policy0_mask_offset = 0x00000010,
|
||||
.policy1_mask_offset = 0x00000014,
|
||||
.policy2_mask_offset = 0x00000018,
|
||||
.policy3_mask_offset = 0x0000001c,
|
||||
.policy0_mask2_offset = 0x00000048,
|
||||
.policy1_mask2_offset = 0x0000004c,
|
||||
.policy2_mask2_offset = 0x00000050,
|
||||
.policy3_mask2_offset = 0x00000054,
|
||||
.lvm_en_offset = 0x00000034,
|
||||
.freq_id = 2,
|
||||
.freq_tbl = slave_axi_freq_tbl,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BCM_SF2_ETH
|
||||
static struct ccu_clock esub_ccu_clk = {
|
||||
.clk = {
|
||||
.name = "esub_ccu_clk",
|
||||
.ops = &ccu_clk_ops,
|
||||
.ccu_clk_mgr_base = ESUB_CLK_BASE_ADDR,
|
||||
},
|
||||
.num_policy_masks = 1,
|
||||
.policy_freq_offset = 0x00000008,
|
||||
.freq_bit_shift = 8,
|
||||
.policy_ctl_offset = 0x0000000c,
|
||||
.policy0_mask_offset = 0x00000010,
|
||||
.policy1_mask_offset = 0x00000014,
|
||||
.policy2_mask_offset = 0x00000018,
|
||||
.policy3_mask_offset = 0x0000001c,
|
||||
.lvm_en_offset = 0x00000034,
|
||||
.freq_id = 2,
|
||||
.freq_tbl = esub_freq_tbl,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Bus clocks
|
||||
*/
|
||||
|
||||
/* KPM bus clocks */
|
||||
static struct bus_clock usb_otg_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "usb_otg_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &usb_otg_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio1_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio1_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio1_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio2_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio2_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio2_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio3_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio3_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio3_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock sdio4_ahb_clk = {
|
||||
.clk = {
|
||||
.name = "sdio4_ahb_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = master_ahb_freq_tbl,
|
||||
.data = &sdio4_ahb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock bsc1_apb_clk = {
|
||||
.clk = {
|
||||
.name = "bsc1_apb_clk",
|
||||
.parent = &kps_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = slave_apb_freq_tbl,
|
||||
.data = &bsc1_apb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock bsc2_apb_clk = {
|
||||
.clk = {
|
||||
.name = "bsc2_apb_clk",
|
||||
.parent = &kps_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = slave_apb_freq_tbl,
|
||||
.data = &bsc2_apb_data,
|
||||
};
|
||||
|
||||
static struct bus_clock bsc3_apb_clk = {
|
||||
.clk = {
|
||||
.name = "bsc3_apb_clk",
|
||||
.parent = &kps_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.freq_tbl = slave_apb_freq_tbl,
|
||||
.data = &bsc3_apb_data,
|
||||
};
|
||||
|
||||
/* KPM peripheral */
|
||||
static struct peri_clock sdio1_clk = {
|
||||
.clk = {
|
||||
.name = "sdio1_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio1_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio2_clk = {
|
||||
.clk = {
|
||||
.name = "sdio2_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio2_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio3_clk = {
|
||||
.clk = {
|
||||
.name = "sdio3_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio3_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio4_clk = {
|
||||
.clk = {
|
||||
.name = "sdio4_clk",
|
||||
.parent = &ref_52m.clk,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio4_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio1_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio1_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio1_sleep_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio2_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio2_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio2_sleep_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio3_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio3_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio3_sleep_data,
|
||||
};
|
||||
|
||||
static struct peri_clock sdio4_sleep_clk = {
|
||||
.clk = {
|
||||
.name = "sdio4_sleep_clk",
|
||||
.parent = &kpm_ccu_clk.clk,
|
||||
.ops = &bus_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &sdio4_sleep_data,
|
||||
};
|
||||
|
||||
/* KPS peripheral clock */
|
||||
static struct peri_clock bsc1_clk = {
|
||||
.clk = {
|
||||
.name = "bsc1_clk",
|
||||
.parent = &ref_13m.clk,
|
||||
.rate = 13 * CLOCK_1M,
|
||||
.div = 1,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &bsc1_data,
|
||||
};
|
||||
|
||||
static struct peri_clock bsc2_clk = {
|
||||
.clk = {
|
||||
.name = "bsc2_clk",
|
||||
.parent = &ref_13m.clk,
|
||||
.rate = 13 * CLOCK_1M,
|
||||
.div = 1,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &bsc2_data,
|
||||
};
|
||||
|
||||
static struct peri_clock bsc3_clk = {
|
||||
.clk = {
|
||||
.name = "bsc3_clk",
|
||||
.parent = &ref_13m.clk,
|
||||
.rate = 13 * CLOCK_1M,
|
||||
.div = 1,
|
||||
.ops = &peri_clk_ops,
|
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR,
|
||||
},
|
||||
.data = &bsc3_data,
|
||||
};
|
||||
|
||||
/* public table for registering clocks */
|
||||
struct clk_lookup arch_clk_tbl[] = {
|
||||
/* Peripheral clocks */
|
||||
CLK_LK(sdio1),
|
||||
CLK_LK(sdio2),
|
||||
CLK_LK(sdio3),
|
||||
CLK_LK(sdio4),
|
||||
CLK_LK(sdio1_sleep),
|
||||
CLK_LK(sdio2_sleep),
|
||||
CLK_LK(sdio3_sleep),
|
||||
CLK_LK(sdio4_sleep),
|
||||
CLK_LK(bsc1),
|
||||
CLK_LK(bsc2),
|
||||
CLK_LK(bsc3),
|
||||
/* Bus clocks */
|
||||
CLK_LK(usb_otg_ahb),
|
||||
CLK_LK(sdio1_ahb),
|
||||
CLK_LK(sdio2_ahb),
|
||||
CLK_LK(sdio3_ahb),
|
||||
CLK_LK(sdio4_ahb),
|
||||
CLK_LK(bsc1_apb),
|
||||
CLK_LK(bsc2_apb),
|
||||
CLK_LK(bsc3_apb),
|
||||
#ifdef CONFIG_BCM_SF2_ETH
|
||||
CLK_LK(esub_ccu),
|
||||
#endif
|
||||
};
|
||||
|
||||
/* public array size */
|
||||
unsigned int arch_clk_tbl_array_size = ARRAY_SIZE(arch_clk_tbl);
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
/* Enable appropriate clocks for a BSC/I2C port */
|
||||
int clk_bsc_enable(void *base)
|
||||
{
|
||||
int ret;
|
||||
char *bscstr, *apbstr;
|
||||
|
||||
switch ((u32) base) {
|
||||
case PMU_BSC_BASE_ADDR:
|
||||
/* PMU clock is always enabled */
|
||||
return 0;
|
||||
case BSC1_BASE_ADDR:
|
||||
bscstr = "bsc1_clk";
|
||||
apbstr = "bsc1_apb_clk";
|
||||
break;
|
||||
case BSC2_BASE_ADDR:
|
||||
bscstr = "bsc2_clk";
|
||||
apbstr = "bsc2_apb_clk";
|
||||
break;
|
||||
case BSC3_BASE_ADDR:
|
||||
bscstr = "bsc3_clk";
|
||||
apbstr = "bsc3_apb_clk";
|
||||
break;
|
||||
default:
|
||||
printf("%s: base 0x%p not found\n", __func__, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Note that the bus clock must be enabled first */
|
||||
|
||||
ret = clk_get_and_enable(apbstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_and_enable(bscstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* bcm281xx architecture clock framework
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <bitfield.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
#define CLK_WR_ACCESS_PASSWORD 0x00a5a501
|
||||
#define WR_ACCESS_OFFSET 0 /* common to all clock blocks */
|
||||
#define POLICY_CTL_GO 1 /* Load and refresh policy masks */
|
||||
#define POLICY_CTL_GO_ATL 4 /* Active Load */
|
||||
|
||||
/* Helper function */
|
||||
int clk_get_and_enable(char *clkstr)
|
||||
{
|
||||
int ret = 0;
|
||||
struct clk *c;
|
||||
|
||||
debug("%s: %s\n", __func__, clkstr);
|
||||
|
||||
c = clk_get(clkstr);
|
||||
if (c) {
|
||||
ret = clk_enable(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
printf("%s: Couldn't find %s\n", __func__, clkstr);
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll a register in a CCU's address space, returning when the
|
||||
* specified bit in that register's value is set (or clear). Delay
|
||||
* a microsecond after each read of the register. Returns true if
|
||||
* successful, or false if we gave up trying.
|
||||
*
|
||||
* Caller must ensure the CCU lock is held.
|
||||
*/
|
||||
#define CLK_GATE_DELAY_USEC 2000
|
||||
static inline int wait_bit(void *base, u32 offset, u32 bit, bool want)
|
||||
{
|
||||
unsigned int tries;
|
||||
u32 bit_mask = 1 << bit;
|
||||
|
||||
for (tries = 0; tries < CLK_GATE_DELAY_USEC; tries++) {
|
||||
u32 val;
|
||||
bool bit_val;
|
||||
|
||||
val = readl(base + offset);
|
||||
bit_val = (val & bit_mask) ? 1 : 0;
|
||||
if (bit_val == want)
|
||||
return 0; /* success */
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
debug("%s: timeout on addr 0x%p, waiting for bit %d to go to %d\n",
|
||||
__func__, base + offset, bit, want);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Enable a peripheral clock */
|
||||
static int peri_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
struct peri_clock *peri_clk = to_peri_clk(c);
|
||||
struct peri_clk_data *cd = peri_clk->data;
|
||||
struct bcm_clk_gate *gate = &cd->gate;
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
|
||||
clk_get_rate(c); /* Make sure rate and sel are filled in */
|
||||
|
||||
/* enable access */
|
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET);
|
||||
|
||||
if (enable) {
|
||||
debug("%s %s set rate %lu div %lu sel %d parent %lu\n",
|
||||
__func__, c->name, c->rate, c->div, c->sel,
|
||||
c->parent->rate);
|
||||
|
||||
/*
|
||||
* clkgate - only software controllable gates are
|
||||
* supported by u-boot which includes all clocks
|
||||
* that matter. This avoids bringing in a lot of extra
|
||||
* complexity as done in the kernel framework.
|
||||
*/
|
||||
if (gate_exists(gate)) {
|
||||
reg = readl(base + cd->gate.offset);
|
||||
reg |= (1 << cd->gate.en_bit);
|
||||
writel(reg, base + cd->gate.offset);
|
||||
}
|
||||
|
||||
/* div and pll select */
|
||||
if (divider_exists(&cd->div)) {
|
||||
reg = readl(base + cd->div.offset);
|
||||
bitfield_replace(reg, cd->div.shift, cd->div.width,
|
||||
c->div - 1);
|
||||
writel(reg, base + cd->div.offset);
|
||||
}
|
||||
|
||||
/* frequency selector */
|
||||
if (selector_exists(&cd->sel)) {
|
||||
reg = readl(base + cd->sel.offset);
|
||||
bitfield_replace(reg, cd->sel.shift, cd->sel.width,
|
||||
c->sel);
|
||||
writel(reg, base + cd->sel.offset);
|
||||
}
|
||||
|
||||
/* trigger */
|
||||
if (trigger_exists(&cd->trig)) {
|
||||
writel((1 << cd->trig.bit), base + cd->trig.offset);
|
||||
|
||||
/* wait for trigger status bit to go to 0 */
|
||||
ret = wait_bit(base, cd->trig.offset, cd->trig.bit, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait for running (status_bit = 1) */
|
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
debug("%s disable clock %s\n", __func__, c->name);
|
||||
|
||||
/* clkgate */
|
||||
reg = readl(base + cd->gate.offset);
|
||||
reg &= ~(1 << cd->gate.en_bit);
|
||||
writel(reg, base + cd->gate.offset);
|
||||
|
||||
/* wait for stop (status_bit = 0) */
|
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, 0);
|
||||
}
|
||||
|
||||
/* disable access */
|
||||
writel(0, base + WR_ACCESS_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the rate of a peripheral clock */
|
||||
static int peri_clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
unsigned long diff;
|
||||
unsigned long new_rate = 0, div = 1;
|
||||
struct peri_clock *peri_clk = to_peri_clk(c);
|
||||
struct peri_clk_data *cd = peri_clk->data;
|
||||
const char **clock;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
diff = rate;
|
||||
|
||||
i = 0;
|
||||
for (clock = cd->clocks; *clock; clock++, i++) {
|
||||
struct refclk *ref = refclk_str_to_clk(*clock);
|
||||
if (!ref) {
|
||||
printf("%s: Lookup of %s failed\n", __func__, *clock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* round to the new rate */
|
||||
div = ref->clk.rate / rate;
|
||||
if (div == 0)
|
||||
div = 1;
|
||||
|
||||
new_rate = ref->clk.rate / div;
|
||||
|
||||
/* get the min diff */
|
||||
if (abs(new_rate - rate) < diff) {
|
||||
diff = abs(new_rate - rate);
|
||||
c->sel = i;
|
||||
c->parent = &ref->clk;
|
||||
c->rate = new_rate;
|
||||
c->div = div;
|
||||
}
|
||||
}
|
||||
|
||||
debug("%s %s set rate %lu div %lu sel %d parent %lu\n", __func__,
|
||||
c->name, c->rate, c->div, c->sel, c->parent->rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the rate of a peripheral clock */
|
||||
static unsigned long peri_clk_get_rate(struct clk *c)
|
||||
{
|
||||
struct peri_clock *peri_clk = to_peri_clk(c);
|
||||
struct peri_clk_data *cd = peri_clk->data;
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
int div = 1;
|
||||
const char **clock;
|
||||
struct refclk *ref;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (selector_exists(&cd->sel)) {
|
||||
reg = readl(base + cd->sel.offset);
|
||||
c->sel = bitfield_extract(reg, cd->sel.shift, cd->sel.width);
|
||||
} else {
|
||||
/*
|
||||
* For peri clocks that don't have a selector, the single
|
||||
* reference clock will always exist at index 0.
|
||||
*/
|
||||
c->sel = 0;
|
||||
}
|
||||
|
||||
if (divider_exists(&cd->div)) {
|
||||
reg = readl(base + cd->div.offset);
|
||||
div = bitfield_extract(reg, cd->div.shift, cd->div.width);
|
||||
div += 1;
|
||||
}
|
||||
|
||||
clock = cd->clocks;
|
||||
ref = refclk_str_to_clk(clock[c->sel]);
|
||||
if (!ref) {
|
||||
printf("%s: Can't lookup %s\n", __func__, clock[c->sel]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->parent = &ref->clk;
|
||||
c->div = div;
|
||||
c->rate = c->parent->rate / c->div;
|
||||
debug("%s parent rate %lu div %d sel %d rate %lu\n", __func__,
|
||||
c->parent->rate, div, c->sel, c->rate);
|
||||
|
||||
return c->rate;
|
||||
}
|
||||
|
||||
/* Peripheral clock operations */
|
||||
struct clk_ops peri_clk_ops = {
|
||||
.enable = peri_clk_enable,
|
||||
.set_rate = peri_clk_set_rate,
|
||||
.get_rate = peri_clk_get_rate,
|
||||
};
|
||||
|
||||
/* Enable a CCU clock */
|
||||
static int ccu_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
struct ccu_clock *ccu_clk = to_ccu_clk(c);
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (!enable)
|
||||
return -EINVAL; /* CCU clock cannot shutdown */
|
||||
|
||||
/* enable access */
|
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET);
|
||||
|
||||
/* config enable for policy engine */
|
||||
writel(1, base + ccu_clk->lvm_en_offset);
|
||||
|
||||
/* wait for bit to go to 0 */
|
||||
ret = wait_bit(base, ccu_clk->lvm_en_offset, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* freq ID */
|
||||
if (!ccu_clk->freq_bit_shift)
|
||||
ccu_clk->freq_bit_shift = 8;
|
||||
|
||||
/* Set frequency id for each of the 4 policies */
|
||||
reg = ccu_clk->freq_id |
|
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift)) |
|
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift * 2)) |
|
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift * 3));
|
||||
writel(reg, base + ccu_clk->policy_freq_offset);
|
||||
|
||||
/* enable all clock mask */
|
||||
writel(0x7fffffff, base + ccu_clk->policy0_mask_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy1_mask_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy2_mask_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy3_mask_offset);
|
||||
|
||||
if (ccu_clk->num_policy_masks == 2) {
|
||||
writel(0x7fffffff, base + ccu_clk->policy0_mask2_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy1_mask2_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy2_mask2_offset);
|
||||
writel(0x7fffffff, base + ccu_clk->policy3_mask2_offset);
|
||||
}
|
||||
|
||||
/* start policy engine */
|
||||
reg = readl(base + ccu_clk->policy_ctl_offset);
|
||||
reg |= (POLICY_CTL_GO + POLICY_CTL_GO_ATL);
|
||||
writel(reg, base + ccu_clk->policy_ctl_offset);
|
||||
|
||||
/* wait till started */
|
||||
ret = wait_bit(base, ccu_clk->policy_ctl_offset, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* disable access */
|
||||
writel(0, base + WR_ACCESS_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the CCU clock rate */
|
||||
static unsigned long ccu_clk_get_rate(struct clk *c)
|
||||
{
|
||||
struct ccu_clock *ccu_clk = to_ccu_clk(c);
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
c->rate = ccu_clk->freq_tbl[ccu_clk->freq_id];
|
||||
return c->rate;
|
||||
}
|
||||
|
||||
/* CCU clock operations */
|
||||
struct clk_ops ccu_clk_ops = {
|
||||
.enable = ccu_clk_enable,
|
||||
.get_rate = ccu_clk_get_rate,
|
||||
};
|
||||
|
||||
/* Enable a bus clock */
|
||||
static int bus_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
struct bus_clock *bus_clk = to_bus_clk(c);
|
||||
struct bus_clk_data *cd = bus_clk->data;
|
||||
void *base = (void *)c->ccu_clk_mgr_base;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
/* enable access */
|
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET);
|
||||
|
||||
/* enable gating */
|
||||
reg = readl(base + cd->gate.offset);
|
||||
if (!!(reg & (1 << cd->gate.status_bit)) == !!enable)
|
||||
debug("%s already %s\n", c->name,
|
||||
enable ? "enabled" : "disabled");
|
||||
else {
|
||||
int want = (enable) ? 1 : 0;
|
||||
reg |= (1 << cd->gate.hw_sw_sel_bit);
|
||||
|
||||
if (enable)
|
||||
reg |= (1 << cd->gate.en_bit);
|
||||
else
|
||||
reg &= ~(1 << cd->gate.en_bit);
|
||||
|
||||
writel(reg, base + cd->gate.offset);
|
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit,
|
||||
want);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable access */
|
||||
writel(0, base + WR_ACCESS_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the rate of a bus clock */
|
||||
static unsigned long bus_clk_get_rate(struct clk *c)
|
||||
{
|
||||
struct bus_clock *bus_clk = to_bus_clk(c);
|
||||
struct ccu_clock *ccu_clk;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
ccu_clk = to_ccu_clk(c->parent);
|
||||
|
||||
c->rate = bus_clk->freq_tbl[ccu_clk->freq_id];
|
||||
c->div = ccu_clk->freq_tbl[ccu_clk->freq_id] / c->rate;
|
||||
return c->rate;
|
||||
}
|
||||
|
||||
/* Bus clock operations */
|
||||
struct clk_ops bus_clk_ops = {
|
||||
.enable = bus_clk_enable,
|
||||
.get_rate = bus_clk_get_rate,
|
||||
};
|
||||
|
||||
/* Enable a reference clock */
|
||||
static int ref_clk_enable(struct clk *c, int enable)
|
||||
{
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reference clock operations */
|
||||
struct clk_ops ref_clk_ops = {
|
||||
.enable = ref_clk_enable,
|
||||
};
|
||||
|
||||
/*
|
||||
* clk.h implementation follows
|
||||
*/
|
||||
|
||||
/* Initialize the clock framework */
|
||||
int clk_init(void)
|
||||
{
|
||||
debug("%s:\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get a clock handle, give a name string */
|
||||
struct clk *clk_get(const char *con_id)
|
||||
{
|
||||
int i;
|
||||
struct clk_lookup *clk_tblp;
|
||||
|
||||
debug("%s: %s\n", __func__, con_id);
|
||||
|
||||
clk_tblp = arch_clk_tbl;
|
||||
for (i = 0; i < arch_clk_tbl_array_size; i++, clk_tblp++) {
|
||||
if (clk_tblp->con_id) {
|
||||
if (!con_id || strcmp(clk_tblp->con_id, con_id))
|
||||
continue;
|
||||
return clk_tblp->clk;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Enable a clock */
|
||||
int clk_enable(struct clk *c)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (!c->ops || !c->ops->enable)
|
||||
return -1;
|
||||
|
||||
/* enable parent clock first */
|
||||
if (c->parent)
|
||||
ret = clk_enable(c->parent);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!c->use_cnt) {
|
||||
c->use_cnt++;
|
||||
ret = c->ops->enable(c, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable a clock */
|
||||
void clk_disable(struct clk *c)
|
||||
{
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
if (!c->ops || !c->ops->enable)
|
||||
return;
|
||||
|
||||
if (c->use_cnt) {
|
||||
c->use_cnt--;
|
||||
c->ops->enable(c, 0);
|
||||
}
|
||||
|
||||
/* disable parent */
|
||||
if (c->parent)
|
||||
clk_disable(c->parent);
|
||||
}
|
||||
|
||||
/* Get the clock rate */
|
||||
unsigned long clk_get_rate(struct clk *c)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (!c || !c->ops || !c->ops->get_rate)
|
||||
return 0;
|
||||
debug("%s: %s\n", __func__, c->name);
|
||||
|
||||
rate = c->ops->get_rate(c);
|
||||
debug("%s: rate = %ld\n", __func__, rate);
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* Set the clock rate */
|
||||
int clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!c || !c->ops || !c->ops->set_rate)
|
||||
return -EINVAL;
|
||||
debug("%s: %s rate=%ld\n", __func__, c->name, rate);
|
||||
|
||||
if (c->use_cnt)
|
||||
return -EINVAL;
|
||||
|
||||
ret = c->ops->set_rate(c, rate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Not required for this arch */
|
||||
/*
|
||||
long clk_round_rate(struct clk *clk, unsigned long rate);
|
||||
int clk_set_parent(struct clk *clk, struct clk *parent);
|
||||
struct clk *clk_get_parent(struct clk *clk);
|
||||
*/
|
||||
@@ -0,0 +1,490 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#ifdef CONFIG_CLK_DEBUG
|
||||
#undef writel
|
||||
#undef readl
|
||||
static inline void writel(u32 val, void *addr)
|
||||
{
|
||||
printf("Write [0x%p] = 0x%08x\n", addr, val);
|
||||
*(u32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline u32 readl(void *addr)
|
||||
{
|
||||
u32 val = *(u32 *)addr;
|
||||
printf("Read [0x%p] = 0x%08x\n", addr, val);
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct clk;
|
||||
|
||||
struct clk_lookup {
|
||||
const char *dev_id;
|
||||
const char *con_id;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
extern struct clk_lookup arch_clk_tbl[];
|
||||
extern unsigned int arch_clk_tbl_array_size;
|
||||
|
||||
/**
|
||||
* struct clk_ops - standard clock operations
|
||||
* @enable: enable/disable clock, see clk_enable() and clk_disable()
|
||||
* @set_rate: set the clock rate, see clk_set_rate().
|
||||
* @get_rate: get the clock rate, see clk_get_rate().
|
||||
* @round_rate: round a given clock rate, see clk_round_rate().
|
||||
* @set_parent: set the clock's parent, see clk_set_parent().
|
||||
*
|
||||
* Group the common clock implementations together so that we
|
||||
* don't have to keep setting the same fiels again. We leave
|
||||
* enable in struct clk.
|
||||
*
|
||||
*/
|
||||
struct clk_ops {
|
||||
int (*enable) (struct clk *c, int enable);
|
||||
int (*set_rate) (struct clk *c, unsigned long rate);
|
||||
unsigned long (*get_rate) (struct clk *c);
|
||||
unsigned long (*round_rate) (struct clk *c, unsigned long rate);
|
||||
int (*set_parent) (struct clk *c, struct clk *parent);
|
||||
};
|
||||
|
||||
struct clk {
|
||||
struct clk *parent;
|
||||
const char *name;
|
||||
int use_cnt;
|
||||
unsigned long rate; /* in HZ */
|
||||
|
||||
/* programmable divider. 0 means fixed ratio to parent clock */
|
||||
unsigned long div;
|
||||
|
||||
struct clk_src *src;
|
||||
struct clk_ops *ops;
|
||||
|
||||
unsigned long ccu_clk_mgr_base;
|
||||
int sel;
|
||||
};
|
||||
|
||||
struct refclk *refclk_str_to_clk(const char *name);
|
||||
|
||||
/* The common clock framework uses u8 to represent a parent index */
|
||||
#define PARENT_COUNT_MAX ((u32)U8_MAX)
|
||||
|
||||
#define BAD_CLK_INDEX U8_MAX /* Can't ever be valid */
|
||||
#define BAD_CLK_NAME ((const char *)-1)
|
||||
|
||||
#define BAD_SCALED_DIV_VALUE U64_MAX
|
||||
|
||||
/*
|
||||
* Utility macros for object flag management. If possible, flags
|
||||
* should be defined such that 0 is the desired default value.
|
||||
*/
|
||||
#define FLAG(type, flag) BCM_CLK_ ## type ## _FLAGS_ ## flag
|
||||
#define FLAG_SET(obj, type, flag) ((obj)->flags |= FLAG(type, flag))
|
||||
#define FLAG_CLEAR(obj, type, flag) ((obj)->flags &= ~(FLAG(type, flag)))
|
||||
#define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag))
|
||||
#define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag)))
|
||||
|
||||
/* Clock field state tests */
|
||||
|
||||
#define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS)
|
||||
#define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED)
|
||||
#define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW)
|
||||
#define gate_is_sw_controllable(gate) FLAG_TEST(gate, GATE, SW)
|
||||
#define gate_is_sw_managed(gate) FLAG_TEST(gate, GATE, SW_MANAGED)
|
||||
#define gate_is_no_disable(gate) FLAG_TEST(gate, GATE, NO_DISABLE)
|
||||
|
||||
#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED)
|
||||
|
||||
#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
|
||||
#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
|
||||
#define divider_has_fraction(div) (!divider_is_fixed(div) && \
|
||||
(div)->frac_width > 0)
|
||||
|
||||
#define selector_exists(sel) ((sel)->width != 0)
|
||||
#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS)
|
||||
|
||||
/* Clock type, used to tell common block what it's part of */
|
||||
enum bcm_clk_type {
|
||||
bcm_clk_none, /* undefined clock type */
|
||||
bcm_clk_bus,
|
||||
bcm_clk_core,
|
||||
bcm_clk_peri
|
||||
};
|
||||
|
||||
/*
|
||||
* Gating control and status is managed by a 32-bit gate register.
|
||||
*
|
||||
* There are several types of gating available:
|
||||
* - (no gate)
|
||||
* A clock with no gate is assumed to be always enabled.
|
||||
* - hardware-only gating (auto-gating)
|
||||
* Enabling or disabling clocks with this type of gate is
|
||||
* managed automatically by the hardware. Such clocks can be
|
||||
* considered by the software to be enabled. The current status
|
||||
* of auto-gated clocks can be read from the gate status bit.
|
||||
* - software-only gating
|
||||
* Auto-gating is not available for this type of clock.
|
||||
* Instead, software manages whether it's enabled by setting or
|
||||
* clearing the enable bit. The current gate status of a gate
|
||||
* under software control can be read from the gate status bit.
|
||||
* To ensure a change to the gating status is complete, the
|
||||
* status bit can be polled to verify that the gate has entered
|
||||
* the desired state.
|
||||
* - selectable hardware or software gating
|
||||
* Gating for this type of clock can be configured to be either
|
||||
* under software or hardware control. Which type is in use is
|
||||
* determined by the hw_sw_sel bit of the gate register.
|
||||
*/
|
||||
struct bcm_clk_gate {
|
||||
u32 offset; /* gate register offset */
|
||||
u32 status_bit; /* 0: gate is disabled; 0: gatge is enabled */
|
||||
u32 en_bit; /* 0: disable; 1: enable */
|
||||
u32 hw_sw_sel_bit; /* 0: hardware gating; 1: software gating */
|
||||
u32 flags; /* BCM_CLK_GATE_FLAGS_* below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Gate flags:
|
||||
* HW means this gate can be auto-gated
|
||||
* SW means the state of this gate can be software controlled
|
||||
* NO_DISABLE means this gate is (only) enabled if under software control
|
||||
* SW_MANAGED means the status of this gate is under software control
|
||||
* ENABLED means this software-managed gate is *supposed* to be enabled
|
||||
*/
|
||||
#define BCM_CLK_GATE_FLAGS_EXISTS ((u32)1 << 0) /* Gate is valid */
|
||||
#define BCM_CLK_GATE_FLAGS_HW ((u32)1 << 1) /* Can auto-gate */
|
||||
#define BCM_CLK_GATE_FLAGS_SW ((u32)1 << 2) /* Software control */
|
||||
#define BCM_CLK_GATE_FLAGS_NO_DISABLE ((u32)1 << 3) /* HW or enabled */
|
||||
#define BCM_CLK_GATE_FLAGS_SW_MANAGED ((u32)1 << 4) /* SW now in control */
|
||||
#define BCM_CLK_GATE_FLAGS_ENABLED ((u32)1 << 5) /* If SW_MANAGED */
|
||||
|
||||
/*
|
||||
* Gate initialization macros.
|
||||
*
|
||||
* Any gate initially under software control will be enabled.
|
||||
*/
|
||||
|
||||
/* A hardware/software gate initially under software control */
|
||||
#define HW_SW_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, SW_MANAGED)|FLAG(GATE, ENABLED)| \
|
||||
FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A hardware/software gate initially under hardware control */
|
||||
#define HW_SW_GATE_AUTO(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A hardware-or-enabled gate (enabled if not under hardware control) */
|
||||
#define HW_ENABLE_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, NO_DISABLE)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A software-only gate */
|
||||
#define SW_ONLY_GATE(_offset, _status_bit, _en_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.flags = FLAG(GATE, SW)|FLAG(GATE, SW_MANAGED)| \
|
||||
FLAG(GATE, ENABLED)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* A hardware-only gate */
|
||||
#define HW_ONLY_GATE(_offset, _status_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Each clock can have zero, one, or two dividers which change the
|
||||
* output rate of the clock. Each divider can be either fixed or
|
||||
* variable. If there are two dividers, they are the "pre-divider"
|
||||
* and the "regular" or "downstream" divider. If there is only one,
|
||||
* there is no pre-divider.
|
||||
*
|
||||
* A fixed divider is any non-zero (positive) value, and it
|
||||
* indicates how the input rate is affected by the divider.
|
||||
*
|
||||
* The value of a variable divider is maintained in a sub-field of a
|
||||
* 32-bit divider register. The position of the field in the
|
||||
* register is defined by its offset and width. The value recorded
|
||||
* in this field is always 1 less than the value it represents.
|
||||
*
|
||||
* In addition, a variable divider can indicate that some subset
|
||||
* of its bits represent a "fractional" part of the divider. Such
|
||||
* bits comprise the low-order portion of the divider field, and can
|
||||
* be viewed as representing the portion of the divider that lies to
|
||||
* the right of the decimal point. Most variable dividers have zero
|
||||
* fractional bits. Variable dividers with non-zero fraction width
|
||||
* still record a value 1 less than the value they represent; the
|
||||
* added 1 does *not* affect the low-order bit in this case, it
|
||||
* affects the bits above the fractional part only. (Often in this
|
||||
* code a divider field value is distinguished from the value it
|
||||
* represents by referring to the latter as a "divisor".)
|
||||
*
|
||||
* In order to avoid dealing with fractions, divider arithmetic is
|
||||
* performed using "scaled" values. A scaled value is one that's
|
||||
* been left-shifted by the fractional width of a divider. Dividing
|
||||
* a scaled value by a scaled divisor produces the desired quotient
|
||||
* without loss of precision and without any other special handling
|
||||
* for fractions.
|
||||
*
|
||||
* The recorded value of a variable divider can be modified. To
|
||||
* modify either divider (or both), a clock must be enabled (i.e.,
|
||||
* using its gate). In addition, a trigger register (described
|
||||
* below) must be used to commit the change, and polled to verify
|
||||
* the change is complete.
|
||||
*/
|
||||
struct bcm_clk_div {
|
||||
union {
|
||||
struct { /* variable divider */
|
||||
u32 offset; /* divider register offset */
|
||||
u32 shift; /* field shift */
|
||||
u32 width; /* field width */
|
||||
u32 frac_width; /* field fraction width */
|
||||
|
||||
u64 scaled_div; /* scaled divider value */
|
||||
};
|
||||
u32 fixed; /* non-zero fixed divider value */
|
||||
};
|
||||
u32 flags; /* BCM_CLK_DIV_FLAGS_* below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Divider flags:
|
||||
* EXISTS means this divider exists
|
||||
* FIXED means it is a fixed-rate divider
|
||||
*/
|
||||
#define BCM_CLK_DIV_FLAGS_EXISTS ((u32)1 << 0) /* Divider is valid */
|
||||
#define BCM_CLK_DIV_FLAGS_FIXED ((u32)1 << 1) /* Fixed-value */
|
||||
|
||||
/* Divider initialization macros */
|
||||
|
||||
/* A fixed (non-zero) divider */
|
||||
#define FIXED_DIVIDER(_value) \
|
||||
{ \
|
||||
.fixed = (_value), \
|
||||
.flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \
|
||||
}
|
||||
|
||||
/* A divider with an integral divisor */
|
||||
#define DIVIDER(_offset, _shift, _width) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.scaled_div = BAD_SCALED_DIV_VALUE, \
|
||||
.flags = FLAG(DIV, EXISTS), \
|
||||
}
|
||||
|
||||
/* A divider whose divisor has an integer and fractional part */
|
||||
#define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.frac_width = (_frac_width), \
|
||||
.scaled_div = BAD_SCALED_DIV_VALUE, \
|
||||
.flags = FLAG(DIV, EXISTS), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Clocks may have multiple "parent" clocks. If there is more than
|
||||
* one, a selector must be specified to define which of the parent
|
||||
* clocks is currently in use. The selected clock is indicated in a
|
||||
* sub-field of a 32-bit selector register. The range of
|
||||
* representable selector values typically exceeds the number of
|
||||
* available parent clocks. Occasionally the reset value of a
|
||||
* selector field is explicitly set to a (specific) value that does
|
||||
* not correspond to a defined input clock.
|
||||
*
|
||||
* We register all known parent clocks with the common clock code
|
||||
* using a packed array (i.e., no empty slots) of (parent) clock
|
||||
* names, and refer to them later using indexes into that array.
|
||||
* We maintain an array of selector values indexed by common clock
|
||||
* index values in order to map between these common clock indexes
|
||||
* and the selector values used by the hardware.
|
||||
*
|
||||
* Like dividers, a selector can be modified, but to do so a clock
|
||||
* must be enabled, and a trigger must be used to commit the change.
|
||||
*/
|
||||
struct bcm_clk_sel {
|
||||
u32 offset; /* selector register offset */
|
||||
u32 shift; /* field shift */
|
||||
u32 width; /* field width */
|
||||
|
||||
u32 parent_count; /* number of entries in parent_sel[] */
|
||||
u32 *parent_sel; /* array of parent selector values */
|
||||
u8 clk_index; /* current selected index in parent_sel[] */
|
||||
};
|
||||
|
||||
/* Selector initialization macro */
|
||||
#define SELECTOR(_offset, _shift, _width) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.clk_index = BAD_CLK_INDEX, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Making changes to a variable divider or a selector for a clock
|
||||
* requires the use of a trigger. A trigger is defined by a single
|
||||
* bit within a register. To signal a change, a 1 is written into
|
||||
* that bit. To determine when the change has been completed, that
|
||||
* trigger bit is polled; the read value will be 1 while the change
|
||||
* is in progress, and 0 when it is complete.
|
||||
*
|
||||
* Occasionally a clock will have more than one trigger. In this
|
||||
* case, the "pre-trigger" will be used when changing a clock's
|
||||
* selector and/or its pre-divider.
|
||||
*/
|
||||
struct bcm_clk_trig {
|
||||
u32 offset; /* trigger register offset */
|
||||
u32 bit; /* trigger bit */
|
||||
u32 flags; /* BCM_CLK_TRIG_FLAGS_* below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Trigger flags:
|
||||
* EXISTS means this trigger exists
|
||||
*/
|
||||
#define BCM_CLK_TRIG_FLAGS_EXISTS ((u32)1 << 0) /* Trigger is valid */
|
||||
|
||||
/* Trigger initialization macro */
|
||||
#define TRIGGER(_offset, _bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.bit = (_bit), \
|
||||
.flags = FLAG(TRIG, EXISTS), \
|
||||
}
|
||||
|
||||
struct bus_clk_data {
|
||||
struct bcm_clk_gate gate;
|
||||
};
|
||||
|
||||
struct core_clk_data {
|
||||
struct bcm_clk_gate gate;
|
||||
};
|
||||
|
||||
struct peri_clk_data {
|
||||
struct bcm_clk_gate gate;
|
||||
struct bcm_clk_trig pre_trig;
|
||||
struct bcm_clk_div pre_div;
|
||||
struct bcm_clk_trig trig;
|
||||
struct bcm_clk_div div;
|
||||
struct bcm_clk_sel sel;
|
||||
const char *clocks[]; /* must be last; use CLOCKS() to declare */
|
||||
};
|
||||
#define CLOCKS(...) { __VA_ARGS__, NULL, }
|
||||
#define NO_CLOCKS { NULL, } /* Must use of no parent clocks */
|
||||
|
||||
struct refclk {
|
||||
struct clk clk;
|
||||
};
|
||||
|
||||
struct peri_clock {
|
||||
struct clk clk;
|
||||
struct peri_clk_data *data;
|
||||
};
|
||||
|
||||
struct ccu_clock {
|
||||
struct clk clk;
|
||||
|
||||
int num_policy_masks;
|
||||
unsigned long policy_freq_offset;
|
||||
int freq_bit_shift; /* 8 for most CCUs */
|
||||
unsigned long policy_ctl_offset;
|
||||
unsigned long policy0_mask_offset;
|
||||
unsigned long policy1_mask_offset;
|
||||
unsigned long policy2_mask_offset;
|
||||
unsigned long policy3_mask_offset;
|
||||
unsigned long policy0_mask2_offset;
|
||||
unsigned long policy1_mask2_offset;
|
||||
unsigned long policy2_mask2_offset;
|
||||
unsigned long policy3_mask2_offset;
|
||||
unsigned long lvm_en_offset;
|
||||
|
||||
int freq_id;
|
||||
unsigned long *freq_tbl;
|
||||
};
|
||||
|
||||
struct bus_clock {
|
||||
struct clk clk;
|
||||
struct bus_clk_data *data;
|
||||
unsigned long *freq_tbl;
|
||||
};
|
||||
|
||||
struct ref_clock {
|
||||
struct clk clk;
|
||||
};
|
||||
|
||||
static inline int is_same_clock(struct clk *a, struct clk *b)
|
||||
{
|
||||
return (a == b);
|
||||
}
|
||||
|
||||
#define to_clk(p) (&((p)->clk))
|
||||
#define name_to_clk(name) (&((name##_clk).clk))
|
||||
/* declare a struct clk_lookup */
|
||||
#define CLK_LK(name) \
|
||||
{.con_id = __stringify(name##_clk), .clk = name_to_clk(name),}
|
||||
|
||||
static inline struct refclk *to_refclk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct refclk, clk);
|
||||
}
|
||||
|
||||
static inline struct peri_clock *to_peri_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct peri_clock, clk);
|
||||
}
|
||||
|
||||
static inline struct ccu_clock *to_ccu_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct ccu_clock, clk);
|
||||
}
|
||||
|
||||
static inline struct bus_clock *to_bus_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct bus_clock, clk);
|
||||
}
|
||||
|
||||
static inline struct ref_clock *to_ref_clk(struct clk *clock)
|
||||
{
|
||||
return container_of(clock, struct ref_clock, clk);
|
||||
}
|
||||
|
||||
extern struct clk_ops peri_clk_ops;
|
||||
extern struct clk_ops ccu_clk_ops;
|
||||
extern struct clk_ops bus_clk_ops;
|
||||
extern struct clk_ops ref_clk_ops;
|
||||
|
||||
extern int clk_get_and_enable(char *clkstr);
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
#define WR_ACCESS_ADDR ESUB_CLK_BASE_ADDR
|
||||
#define WR_ACCESS_PASSWORD 0xA5A500
|
||||
|
||||
#define PLLE_POST_RESETB_ADDR (ESUB_CLK_BASE_ADDR + 0x00000C00)
|
||||
|
||||
#define PLLE_RESETB_ADDR (ESUB_CLK_BASE_ADDR + 0x00000C58)
|
||||
#define PLLE_RESETB_I_PLL_RESETB_PLLE_MASK 0x00010000
|
||||
#define PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK 0x00000001
|
||||
|
||||
#define PLL_LOCK_ADDR (ESUB_CLK_BASE_ADDR + 0x00000C38)
|
||||
#define PLL_LOCK_PLL_LOCK_PLLE_MASK 0x00000001
|
||||
|
||||
#define ESW_SYS_DIV_ADDR (ESUB_CLK_BASE_ADDR + 0x00000A04)
|
||||
#define ESW_SYS_DIV_PLL_SELECT_MASK 0x00000300
|
||||
#define ESW_SYS_DIV_DIV_MASK 0x0000001C
|
||||
#define ESW_SYS_DIV_PLL_VAR_208M_CLK_SELECT 0x00000100
|
||||
#define ESW_SYS_DIV_DIV_SELECT 0x4
|
||||
#define ESW_SYS_DIV_TRIGGER_MASK 0x00000001
|
||||
|
||||
#define ESUB_AXI_DIV_DEBUG_ADDR (ESUB_CLK_BASE_ADDR + 0x00000E04)
|
||||
#define ESUB_AXI_DIV_DEBUG_PLL_SELECT_MASK 0x0000001C
|
||||
#define ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK 0x00000040
|
||||
#define ESUB_AXI_DIV_DEBUG_PLL_VAR_208M_CLK_SELECT 0x0
|
||||
#define ESUB_AXI_DIV_DEBUG_TRIGGER_MASK 0x00000001
|
||||
|
||||
#define PLL_MAX_RETRY 100
|
||||
|
||||
/* Enable appropriate clocks for Ethernet */
|
||||
int clk_eth_enable(void)
|
||||
{
|
||||
int rc = -1;
|
||||
int retry_count = 0;
|
||||
rc = clk_get_and_enable("esub_ccu_clk");
|
||||
|
||||
/* Enable Access to CCU registers */
|
||||
writel((1 | WR_ACCESS_PASSWORD), WR_ACCESS_ADDR);
|
||||
|
||||
writel(readl(PLLE_POST_RESETB_ADDR) &
|
||||
~PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK,
|
||||
PLLE_POST_RESETB_ADDR);
|
||||
|
||||
/* Take PLL out of reset and put into normal mode */
|
||||
writel(readl(PLLE_RESETB_ADDR) | PLLE_RESETB_I_PLL_RESETB_PLLE_MASK,
|
||||
PLLE_RESETB_ADDR);
|
||||
|
||||
/* Wait for PLL lock */
|
||||
rc = -1;
|
||||
while (retry_count < PLL_MAX_RETRY) {
|
||||
udelay(100);
|
||||
if (readl(PLL_LOCK_ADDR) & PLL_LOCK_PLL_LOCK_PLLE_MASK) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
printf("%s: ETH-PLL lock timeout, Ethernet is not enabled!\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
writel(readl(PLLE_POST_RESETB_ADDR) |
|
||||
PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK,
|
||||
PLLE_POST_RESETB_ADDR);
|
||||
|
||||
/* Switch esw_sys_clk to use 104MHz(208MHz/2) clock */
|
||||
writel((readl(ESW_SYS_DIV_ADDR) &
|
||||
~(ESW_SYS_DIV_PLL_SELECT_MASK | ESW_SYS_DIV_DIV_MASK)) |
|
||||
ESW_SYS_DIV_PLL_VAR_208M_CLK_SELECT | ESW_SYS_DIV_DIV_SELECT,
|
||||
ESW_SYS_DIV_ADDR);
|
||||
|
||||
writel(readl(ESW_SYS_DIV_ADDR) | ESW_SYS_DIV_TRIGGER_MASK,
|
||||
ESW_SYS_DIV_ADDR);
|
||||
|
||||
/* Wait for trigger complete */
|
||||
rc = -1;
|
||||
retry_count = 0;
|
||||
while (retry_count < PLL_MAX_RETRY) {
|
||||
udelay(100);
|
||||
if (!(readl(ESW_SYS_DIV_ADDR) & ESW_SYS_DIV_TRIGGER_MASK)) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
printf("%s: SYS CLK Trigger timeout, Ethernet is not enabled!\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* switch Esub AXI clock to 208MHz */
|
||||
writel((readl(ESUB_AXI_DIV_DEBUG_ADDR) &
|
||||
~(ESUB_AXI_DIV_DEBUG_PLL_SELECT_MASK |
|
||||
ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK |
|
||||
ESUB_AXI_DIV_DEBUG_TRIGGER_MASK)) |
|
||||
ESUB_AXI_DIV_DEBUG_PLL_VAR_208M_CLK_SELECT |
|
||||
ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK,
|
||||
ESUB_AXI_DIV_DEBUG_ADDR);
|
||||
|
||||
writel(readl(ESUB_AXI_DIV_DEBUG_ADDR) |
|
||||
ESUB_AXI_DIV_DEBUG_TRIGGER_MASK,
|
||||
ESUB_AXI_DIV_DEBUG_ADDR);
|
||||
|
||||
/* Wait for trigger complete */
|
||||
rc = -1;
|
||||
retry_count = 0;
|
||||
while (retry_count < PLL_MAX_RETRY) {
|
||||
udelay(100);
|
||||
if (!(readl(ESUB_AXI_DIV_DEBUG_ADDR) &
|
||||
ESUB_AXI_DIV_DEBUG_TRIGGER_MASK)) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
printf("%s: AXI CLK Trigger timeout, Ethernet is not enabled!\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Disable Access to CCU registers */
|
||||
writel(WR_ACCESS_PASSWORD, WR_ACCESS_ADDR);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
/* Enable appropriate clocks for an SDIO port */
|
||||
int clk_sdio_enable(void *base, u32 rate, u32 *actual_ratep)
|
||||
{
|
||||
int ret;
|
||||
struct clk *c;
|
||||
|
||||
char *clkstr;
|
||||
char *slpstr;
|
||||
char *ahbstr;
|
||||
|
||||
switch ((u32) base) {
|
||||
case CONFIG_SYS_SDIO_BASE0:
|
||||
clkstr = CONFIG_SYS_SDIO0 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO0 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO0 "_sleep_clk";
|
||||
break;
|
||||
case CONFIG_SYS_SDIO_BASE1:
|
||||
clkstr = CONFIG_SYS_SDIO1 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO1 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO1 "_sleep_clk";
|
||||
break;
|
||||
case CONFIG_SYS_SDIO_BASE2:
|
||||
clkstr = CONFIG_SYS_SDIO2 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO2 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO2 "_sleep_clk";
|
||||
break;
|
||||
case CONFIG_SYS_SDIO_BASE3:
|
||||
clkstr = CONFIG_SYS_SDIO3 "_clk";
|
||||
ahbstr = CONFIG_SYS_SDIO3 "_ahb_clk";
|
||||
slpstr = CONFIG_SYS_SDIO3 "_sleep_clk";
|
||||
break;
|
||||
default:
|
||||
printf("%s: base 0x%p not found\n", __func__, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_get_and_enable(ahbstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_and_enable(slpstr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c = clk_get(clkstr);
|
||||
if (c) {
|
||||
ret = clk_set_rate(c, rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
printf("%s: Couldn't find %s\n", __func__, clkstr);
|
||||
return -EINVAL;
|
||||
}
|
||||
*actual_ratep = rate;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include "clk-core.h"
|
||||
|
||||
/* Enable appropriate clocks for the USB OTG port */
|
||||
int clk_usb_otg_enable(void *base)
|
||||
{
|
||||
char *ahbstr;
|
||||
|
||||
switch ((u32) base) {
|
||||
case HSOTG_BASE_ADDR:
|
||||
ahbstr = "usb_otg_ahb_clk";
|
||||
break;
|
||||
default:
|
||||
printf("%s: base 0x%p not found\n", __func__, base);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return clk_get_and_enable(ahbstr);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
|
||||
#define EN_MASK 0x08000000 /* Enable timer */
|
||||
#define SRSTEN_MASK 0x04000000 /* Enable soft reset */
|
||||
#define CLKS_SHIFT 20 /* Clock period shift */
|
||||
#define LD_SHIFT 0 /* Reload value shift */
|
||||
|
||||
void reset_cpu(ulong ignored)
|
||||
{
|
||||
/*
|
||||
* Set WD enable, RST enable,
|
||||
* 3.9 msec clock period (8), reload value (8*3.9ms)
|
||||
*/
|
||||
u32 reg = EN_MASK + SRSTEN_MASK + (8 << CLKS_SHIFT) + (8 << LD_SHIFT);
|
||||
writel(reg, SECWD2_BASE_ADDR);
|
||||
|
||||
while (1)
|
||||
; /* loop forever till reset */
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2014 Broadcom Corporation.
|
||||
|
||||
obj-y += reset.o
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define CRMU_MAIL_BOX1 0x03024028
|
||||
#define CRMU_SOFT_RESET_CMD 0xFFFFFFFF
|
||||
|
||||
void reset_cpu(ulong ignored)
|
||||
{
|
||||
/* Send soft reset command via Mailbox. */
|
||||
writel(CRMU_SOFT_RESET_CMD, CRMU_MAIL_BOX1);
|
||||
|
||||
while (1)
|
||||
; /* loop forever till reset */
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2014 Broadcom Corporation.
|
||||
|
||||
obj-y += reset.o
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define CRU_RESET_OFFSET 0x1803F184
|
||||
|
||||
void reset_cpu(ulong ignored)
|
||||
{
|
||||
/* Reset the cpu by setting software reset request bit */
|
||||
writel(0x1, CRU_RESET_OFFSET);
|
||||
|
||||
while (1)
|
||||
; /* loop forever till reset */
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2010
|
||||
* Texas Instruments, <www.ti.com>
|
||||
* Aneesh V <aneesh@ti.com>
|
||||
*/
|
||||
#include <cpu_func.h>
|
||||
#include <linux/types.h>
|
||||
#include <common.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/utils.h>
|
||||
|
||||
#define ARMV7_DCACHE_INVAL_RANGE 1
|
||||
#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 2
|
||||
|
||||
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
|
||||
|
||||
/* Asm functions from cache_v7_asm.S */
|
||||
void v7_flush_dcache_all(void);
|
||||
void v7_invalidate_dcache_all(void);
|
||||
|
||||
static u32 get_ccsidr(void)
|
||||
{
|
||||
u32 ccsidr;
|
||||
|
||||
/* Read current CP15 Cache Size ID Register */
|
||||
asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
|
||||
return ccsidr;
|
||||
}
|
||||
|
||||
static void v7_dcache_clean_inval_range(u32 start, u32 stop, u32 line_len)
|
||||
{
|
||||
u32 mva;
|
||||
|
||||
/* Align start to cache line boundary */
|
||||
start &= ~(line_len - 1);
|
||||
for (mva = start; mva < stop; mva = mva + line_len) {
|
||||
/* DCCIMVAC - Clean & Invalidate data cache by MVA to PoC */
|
||||
asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva));
|
||||
}
|
||||
}
|
||||
|
||||
static void v7_dcache_inval_range(u32 start, u32 stop, u32 line_len)
|
||||
{
|
||||
u32 mva;
|
||||
|
||||
if (!check_cache_range(start, stop))
|
||||
return;
|
||||
|
||||
for (mva = start; mva < stop; mva = mva + line_len) {
|
||||
/* DCIMVAC - Invalidate data cache by MVA to PoC */
|
||||
asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva));
|
||||
}
|
||||
}
|
||||
|
||||
static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op)
|
||||
{
|
||||
u32 line_len, ccsidr;
|
||||
|
||||
ccsidr = get_ccsidr();
|
||||
line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >>
|
||||
CCSIDR_LINE_SIZE_OFFSET) + 2;
|
||||
/* Converting from words to bytes */
|
||||
line_len += 2;
|
||||
/* converting from log2(linelen) to linelen */
|
||||
line_len = 1 << line_len;
|
||||
|
||||
switch (range_op) {
|
||||
case ARMV7_DCACHE_CLEAN_INVAL_RANGE:
|
||||
v7_dcache_clean_inval_range(start, stop, line_len);
|
||||
break;
|
||||
case ARMV7_DCACHE_INVAL_RANGE:
|
||||
v7_dcache_inval_range(start, stop, line_len);
|
||||
break;
|
||||
}
|
||||
|
||||
/* DSB to make sure the operation is complete */
|
||||
dsb();
|
||||
}
|
||||
|
||||
/* Invalidate TLB */
|
||||
static void v7_inval_tlb(void)
|
||||
{
|
||||
/* Invalidate entire unified TLB */
|
||||
asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0));
|
||||
/* Invalidate entire data TLB */
|
||||
asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0));
|
||||
/* Invalidate entire instruction TLB */
|
||||
asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0));
|
||||
/* Full system DSB - make sure that the invalidation is complete */
|
||||
dsb();
|
||||
/* Full system ISB - make sure the instruction stream sees it */
|
||||
isb();
|
||||
}
|
||||
|
||||
void invalidate_dcache_all(void)
|
||||
{
|
||||
v7_invalidate_dcache_all();
|
||||
|
||||
v7_outer_cache_inval_all();
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs a clean & invalidation of the entire data cache
|
||||
* at all levels
|
||||
*/
|
||||
void flush_dcache_all(void)
|
||||
{
|
||||
v7_flush_dcache_all();
|
||||
|
||||
v7_outer_cache_flush_all();
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidates range in all levels of D-cache/unified cache used:
|
||||
* Affects the range [start, stop - 1]
|
||||
*/
|
||||
void invalidate_dcache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned int align = 0;
|
||||
|
||||
if (!IS_ALIGNED(start, CONFIG_SYS_CACHELINE_SIZE)){
|
||||
align = 1;
|
||||
}
|
||||
|
||||
if (!IS_ALIGNED(stop, CONFIG_SYS_CACHELINE_SIZE)){
|
||||
align = 1;
|
||||
}
|
||||
|
||||
if (!align){
|
||||
v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE);
|
||||
}
|
||||
else{
|
||||
u32 line_len, ccsidr;
|
||||
|
||||
ccsidr = get_ccsidr();
|
||||
line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >>
|
||||
CCSIDR_LINE_SIZE_OFFSET) + 2;
|
||||
/* Converting from words to bytes */
|
||||
line_len += 2;
|
||||
/* converting from log2(linelen) to linelen */
|
||||
line_len = 1 << line_len;
|
||||
|
||||
v7_dcache_clean_inval_range(start, stop, line_len);
|
||||
}
|
||||
|
||||
v7_outer_cache_inval_range(start, stop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush range(clean & invalidate) from all levels of D-cache/unified
|
||||
* cache used:
|
||||
* Affects the range [start, stop - 1]
|
||||
*/
|
||||
void flush_dcache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE);
|
||||
|
||||
v7_outer_cache_flush_range(start, stop);
|
||||
}
|
||||
|
||||
void arm_init_before_mmu(void)
|
||||
{
|
||||
v7_outer_cache_enable();
|
||||
invalidate_dcache_all();
|
||||
v7_inval_tlb();
|
||||
}
|
||||
|
||||
void mmu_page_table_flush(unsigned long start, unsigned long stop)
|
||||
{
|
||||
flush_dcache_range(start, stop);
|
||||
v7_inval_tlb();
|
||||
}
|
||||
#else /* #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) */
|
||||
void invalidate_dcache_all(void)
|
||||
{
|
||||
}
|
||||
|
||||
void flush_dcache_all(void)
|
||||
{
|
||||
}
|
||||
|
||||
void invalidate_dcache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
|
||||
void flush_dcache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
|
||||
void arm_init_before_mmu(void)
|
||||
{
|
||||
}
|
||||
|
||||
void mmu_page_table_flush(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
|
||||
void arm_init_domains(void)
|
||||
{
|
||||
}
|
||||
#endif /* #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) */
|
||||
|
||||
#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
|
||||
/* Invalidate entire I-cache and branch predictor array */
|
||||
void invalidate_icache_all(void)
|
||||
{
|
||||
/*
|
||||
* Invalidate all instruction caches to PoU.
|
||||
* Also flushes branch target cache.
|
||||
*/
|
||||
asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0));
|
||||
|
||||
/* Invalidate entire branch predictor array */
|
||||
asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0));
|
||||
|
||||
/* Full system DSB - make sure that the invalidation is complete */
|
||||
dsb();
|
||||
|
||||
/* ISB - make sure the instruction stream sees it */
|
||||
isb();
|
||||
}
|
||||
#else
|
||||
void invalidate_icache_all(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Stub implementations for outer cache operations */
|
||||
__weak void v7_outer_cache_enable(void) {}
|
||||
__weak void v7_outer_cache_disable(void) {}
|
||||
__weak void v7_outer_cache_flush_all(void) {}
|
||||
__weak void v7_outer_cache_inval_all(void) {}
|
||||
__weak void v7_outer_cache_flush_range(u32 start, u32 end) {}
|
||||
__weak void v7_outer_cache_inval_range(u32 start, u32 end) {}
|
||||
@@ -0,0 +1,152 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
|
||||
#define ARM(x...)
|
||||
#define THUMB(x...) x
|
||||
#else
|
||||
#define ARM(x...) x
|
||||
#define THUMB(x...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* v7_flush_dcache_all()
|
||||
*
|
||||
* Flush the whole D-cache.
|
||||
*
|
||||
* Corrupted registers: r0-r7, r9-r11 (r6 only in Thumb mode)
|
||||
*
|
||||
* Note: copied from arch/arm/mm/cache-v7.S of Linux 4.4
|
||||
*/
|
||||
ENTRY(__v7_flush_dcache_all)
|
||||
dmb @ ensure ordering with previous memory accesses
|
||||
mrc p15, 1, r0, c0, c0, 1 @ read clidr
|
||||
mov r3, r0, lsr #23 @ move LoC into position
|
||||
ands r3, r3, #7 << 1 @ extract LoC*2 from clidr
|
||||
beq finished @ if loc is 0, then no need to clean
|
||||
start_flush_levels:
|
||||
mov r10, #0 @ start clean at cache level 0
|
||||
flush_levels:
|
||||
add r2, r10, r10, lsr #1 @ work out 3x current cache level
|
||||
mov r1, r0, lsr r2 @ extract cache type bits from clidr
|
||||
and r1, r1, #7 @ mask of the bits for current cache only
|
||||
cmp r1, #2 @ see what cache we have at this level
|
||||
blt skip @ skip if no cache, or just i-cache
|
||||
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
|
||||
isb @ isb to sych the new cssr&csidr
|
||||
mrc p15, 1, r1, c0, c0, 0 @ read the new csidr
|
||||
and r2, r1, #7 @ extract the length of the cache lines
|
||||
add r2, r2, #4 @ add 4 (line length offset)
|
||||
movw r4, #0x3ff
|
||||
ands r4, r4, r1, lsr #3 @ find maximum number on the way size
|
||||
clz r5, r4 @ find bit position of way size increment
|
||||
movw r7, #0x7fff
|
||||
ands r7, r7, r1, lsr #13 @ extract max number of the index size
|
||||
loop1:
|
||||
mov r9, r7 @ create working copy of max index
|
||||
loop2:
|
||||
ARM( orr r11, r10, r4, lsl r5 ) @ factor way and cache number into r11
|
||||
THUMB( lsl r6, r4, r5 )
|
||||
THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11
|
||||
ARM( orr r11, r11, r9, lsl r2 ) @ factor index number into r11
|
||||
THUMB( lsl r6, r9, r2 )
|
||||
THUMB( orr r11, r11, r6 ) @ factor index number into r11
|
||||
mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way
|
||||
subs r9, r9, #1 @ decrement the index
|
||||
bge loop2
|
||||
subs r4, r4, #1 @ decrement the way
|
||||
bge loop1
|
||||
skip:
|
||||
add r10, r10, #2 @ increment cache number
|
||||
cmp r3, r10
|
||||
bgt flush_levels
|
||||
finished:
|
||||
mov r10, #0 @ swith back to cache level 0
|
||||
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
|
||||
dsb st
|
||||
isb
|
||||
bx lr
|
||||
ENDPROC(__v7_flush_dcache_all)
|
||||
|
||||
ENTRY(v7_flush_dcache_all)
|
||||
ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
|
||||
THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
|
||||
bl __v7_flush_dcache_all
|
||||
ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
|
||||
THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
|
||||
bx lr
|
||||
ENDPROC(v7_flush_dcache_all)
|
||||
|
||||
/*
|
||||
* v7_invalidate_dcache_all()
|
||||
*
|
||||
* Invalidate the whole D-cache.
|
||||
*
|
||||
* Corrupted registers: r0-r7, r9-r11 (r6 only in Thumb mode)
|
||||
*
|
||||
* Note: copied from __v7_flush_dcache_all above with
|
||||
* mcr p15, 0, r11, c7, c14, 2
|
||||
* Replaced with:
|
||||
* mcr p15, 0, r11, c7, c6, 2
|
||||
*/
|
||||
ENTRY(__v7_invalidate_dcache_all)
|
||||
dmb @ ensure ordering with previous memory accesses
|
||||
mrc p15, 1, r0, c0, c0, 1 @ read clidr
|
||||
mov r3, r0, lsr #23 @ move LoC into position
|
||||
ands r3, r3, #7 << 1 @ extract LoC*2 from clidr
|
||||
beq inval_finished @ if loc is 0, then no need to clean
|
||||
mov r10, #0 @ start clean at cache level 0
|
||||
inval_levels:
|
||||
add r2, r10, r10, lsr #1 @ work out 3x current cache level
|
||||
mov r1, r0, lsr r2 @ extract cache type bits from clidr
|
||||
and r1, r1, #7 @ mask of the bits for current cache only
|
||||
cmp r1, #2 @ see what cache we have at this level
|
||||
blt inval_skip @ skip if no cache, or just i-cache
|
||||
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
|
||||
isb @ isb to sych the new cssr&csidr
|
||||
mrc p15, 1, r1, c0, c0, 0 @ read the new csidr
|
||||
and r2, r1, #7 @ extract the length of the cache lines
|
||||
add r2, r2, #4 @ add 4 (line length offset)
|
||||
movw r4, #0x3ff
|
||||
ands r4, r4, r1, lsr #3 @ find maximum number on the way size
|
||||
clz r5, r4 @ find bit position of way size increment
|
||||
movw r7, #0x7fff
|
||||
ands r7, r7, r1, lsr #13 @ extract max number of the index size
|
||||
inval_loop1:
|
||||
mov r9, r7 @ create working copy of max index
|
||||
inval_loop2:
|
||||
ARM( orr r11, r10, r4, lsl r5 ) @ factor way and cache number into r11
|
||||
THUMB( lsl r6, r4, r5 )
|
||||
THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11
|
||||
ARM( orr r11, r11, r9, lsl r2 ) @ factor index number into r11
|
||||
THUMB( lsl r6, r9, r2 )
|
||||
THUMB( orr r11, r11, r6 ) @ factor index number into r11
|
||||
mcr p15, 0, r11, c7, c6, 2 @ invalidate by set/way
|
||||
subs r9, r9, #1 @ decrement the index
|
||||
bge inval_loop2
|
||||
subs r4, r4, #1 @ decrement the way
|
||||
bge inval_loop1
|
||||
inval_skip:
|
||||
add r10, r10, #2 @ increment cache number
|
||||
cmp r3, r10
|
||||
bgt inval_levels
|
||||
inval_finished:
|
||||
mov r10, #0 @ swith back to cache level 0
|
||||
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
|
||||
dsb st
|
||||
isb
|
||||
bx lr
|
||||
ENDPROC(__v7_invalidate_dcache_all)
|
||||
|
||||
ENTRY(v7_invalidate_dcache_all)
|
||||
ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
|
||||
THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
|
||||
bl __v7_invalidate_dcache_all
|
||||
ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
|
||||
THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
|
||||
bx lr
|
||||
ENDPROC(v7_invalidate_dcache_all)
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2002
|
||||
# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
|
||||
# On supported platforms we set the bit which causes us to trap on unaligned
|
||||
# memory access. This is the opposite of what the compiler expects to be
|
||||
# the default so we must pass in -mno-unaligned-access so that it is aware
|
||||
# of our decision.
|
||||
PF_NO_UNALIGNED := $(call cc-option, -mno-unaligned-access,)
|
||||
PLATFORM_CPPFLAGS += $(PF_NO_UNALIGNED)
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2015 Texas Insturments
|
||||
*/
|
||||
|
||||
/*
|
||||
* CP15 specific code
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
void __weak v7_arch_cp15_set_l2aux_ctrl(u32 l2actlr, u32 cpu_midr,
|
||||
u32 cpu_rev_comb, u32 cpu_variant,
|
||||
u32 cpu_rev)
|
||||
{
|
||||
asm volatile ("mcr p15, 1, %0, c15, c0, 0\n\t" : : "r"(l2actlr));
|
||||
}
|
||||
|
||||
void __weak v7_arch_cp15_set_acr(u32 acr, u32 cpu_midr, u32 cpu_rev_comb,
|
||||
u32 cpu_variant, u32 cpu_rev)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c1, c0, 1\n\t" : : "r"(acr));
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2008 Texas Insturments
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Marius Groeger <mgroeger@sysgo.de>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* CPU specific code
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <cpu_func.h>
|
||||
#include <irq_func.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
void __weak cpu_cache_initialization(void){}
|
||||
|
||||
int cleanup_before_linux_select(int flags)
|
||||
{
|
||||
/*
|
||||
* this function is called just before we call linux
|
||||
* it prepares the processor for linux
|
||||
*
|
||||
* we turn off caches etc ...
|
||||
*/
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
disable_interrupts();
|
||||
#endif
|
||||
|
||||
if (flags & CBL_DISABLE_CACHES) {
|
||||
/*
|
||||
* turn off D-cache
|
||||
* dcache_disable() in turn flushes the d-cache and disables MMU
|
||||
*/
|
||||
dcache_disable();
|
||||
v7_outer_cache_disable();
|
||||
|
||||
/*
|
||||
* After D-cache is flushed and before it is disabled there may
|
||||
* be some new valid entries brought into the cache. We are
|
||||
* sure that these lines are not dirty and will not affect our
|
||||
* execution. (because unwinding the call-stack and setting a
|
||||
* bit in CP15 SCTRL is all we did during this. We have not
|
||||
* pushed anything on to the stack. Neither have we affected
|
||||
* any static data) So just invalidate the entire d-cache again
|
||||
* to avoid coherency problems for kernel
|
||||
*/
|
||||
invalidate_dcache_all();
|
||||
|
||||
icache_disable();
|
||||
invalidate_icache_all();
|
||||
} else {
|
||||
/*
|
||||
* Turn off I-cache and invalidate it
|
||||
*/
|
||||
icache_disable();
|
||||
invalidate_icache_all();
|
||||
|
||||
flush_dcache_all();
|
||||
invalidate_icache_all();
|
||||
icache_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Some CPU need more cache attention before starting the kernel.
|
||||
*/
|
||||
cpu_cache_initialization();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cleanup_before_linux(void)
|
||||
{
|
||||
return cleanup_before_linux_select(CBL_ALL);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Switch to non-secure mode
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt
|
||||
*
|
||||
* This module contains the ARMv7 specific code required for leaving the
|
||||
* secure mode before booting an operating system.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <bootm.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/secure.h>
|
||||
#include <asm/setjmp.h>
|
||||
|
||||
/**
|
||||
* entry_non_secure() - entry point when switching to non-secure mode
|
||||
*
|
||||
* When switching to non-secure mode switch_to_non_secure_mode() calls this
|
||||
* function passing a jump buffer. We use this jump buffer to restore the
|
||||
* original stack and register state.
|
||||
*
|
||||
* @non_secure_jmp: jump buffer for restoring stack and registers
|
||||
*/
|
||||
static void entry_non_secure(struct jmp_buf_data *non_secure_jmp)
|
||||
{
|
||||
dcache_enable();
|
||||
debug("Reached non-secure mode\n");
|
||||
|
||||
/* Restore stack and registers saved in switch_to_non_secure_mode() */
|
||||
longjmp(non_secure_jmp, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* switch_to_non_secure_mode() - switch to non-secure mode
|
||||
*
|
||||
* Operating systems may expect to run in non-secure mode. Here we check if
|
||||
* we are running in secure mode and switch to non-secure mode if necessary.
|
||||
*/
|
||||
void switch_to_non_secure_mode(void)
|
||||
{
|
||||
static bool is_nonsec;
|
||||
struct jmp_buf_data non_secure_jmp;
|
||||
|
||||
if (armv7_boot_nonsec() && !is_nonsec) {
|
||||
if (setjmp(&non_secure_jmp))
|
||||
return;
|
||||
dcache_disable(); /* flush cache before switch to HYP */
|
||||
armv7_init_nonsec();
|
||||
is_nonsec = true;
|
||||
secure_ram_addr(_do_nonsec_entry)(entry_non_secure,
|
||||
(uintptr_t)&non_secure_jmp,
|
||||
0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2014 Broadcom Corporation.
|
||||
|
||||
obj-y += armpll.o
|
||||
obj-y += hwinit-common.o
|
||||
obj-y += timer.o
|
||||
@@ -0,0 +1,169 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/iproc-common/armpll.h>
|
||||
#include <asm/iproc-common/sysmap.h>
|
||||
|
||||
#define NELEMS(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
struct armpll_parameters {
|
||||
unsigned int mode;
|
||||
unsigned int ndiv_int;
|
||||
unsigned int ndiv_frac;
|
||||
unsigned int pdiv;
|
||||
unsigned int freqid;
|
||||
};
|
||||
|
||||
struct armpll_parameters armpll_clk_tab[] = {
|
||||
{ 25, 64, 1, 1, 0},
|
||||
{ 100, 64, 1, 1, 2},
|
||||
{ 400, 64, 1, 1, 6},
|
||||
{ 448, 71, 713050, 1, 6},
|
||||
{ 500, 80, 1, 1, 6},
|
||||
{ 560, 89, 629145, 1, 6},
|
||||
{ 600, 96, 1, 1, 6},
|
||||
{ 800, 64, 1, 1, 7},
|
||||
{ 896, 71, 713050, 1, 7},
|
||||
{ 1000, 80, 1, 1, 7},
|
||||
{ 1100, 88, 1, 1, 7},
|
||||
{ 1120, 89, 629145, 1, 7},
|
||||
{ 1200, 96, 1, 1, 7},
|
||||
};
|
||||
|
||||
uint32_t armpll_config(uint32_t clkmhz)
|
||||
{
|
||||
uint32_t freqid;
|
||||
uint32_t ndiv_frac;
|
||||
uint32_t pll;
|
||||
uint32_t status = 1;
|
||||
uint32_t timeout_countdown;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NELEMS(armpll_clk_tab); i++) {
|
||||
if (armpll_clk_tab[i].mode == clkmhz) {
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status) {
|
||||
printf("Error: Clock configuration not supported\n");
|
||||
goto armpll_config_done;
|
||||
}
|
||||
|
||||
/* Enable write access */
|
||||
writel(IPROC_REG_WRITE_ACCESS, IHOST_PROC_CLK_WR_ACCESS);
|
||||
|
||||
if (clkmhz == 25)
|
||||
freqid = 0;
|
||||
else
|
||||
freqid = 2;
|
||||
|
||||
/* Bypass ARM clock and run on sysclk */
|
||||
writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE |
|
||||
freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R |
|
||||
freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R |
|
||||
freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R |
|
||||
freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R,
|
||||
IHOST_PROC_CLK_POLICY_FREQ);
|
||||
|
||||
writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO |
|
||||
1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC,
|
||||
IHOST_PROC_CLK_POLICY_CTL);
|
||||
|
||||
/* Poll CCU until operation complete */
|
||||
timeout_countdown = 0x100000;
|
||||
while (readl(IHOST_PROC_CLK_POLICY_CTL) &
|
||||
(1 << IHOST_PROC_CLK_POLICY_CTL__GO)) {
|
||||
timeout_countdown--;
|
||||
if (timeout_countdown == 0) {
|
||||
printf("CCU polling timedout\n");
|
||||
status = 1;
|
||||
goto armpll_config_done;
|
||||
}
|
||||
}
|
||||
|
||||
if (clkmhz == 25 || clkmhz == 100) {
|
||||
status = 0;
|
||||
goto armpll_config_done;
|
||||
}
|
||||
|
||||
/* Now it is safe to program the PLL */
|
||||
pll = readl(IHOST_PROC_CLK_PLLARMB);
|
||||
pll &= ~((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1);
|
||||
ndiv_frac =
|
||||
((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1) &
|
||||
(armpll_clk_tab[i].ndiv_frac <<
|
||||
IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_R);
|
||||
pll |= ndiv_frac;
|
||||
writel(pll, IHOST_PROC_CLK_PLLARMB);
|
||||
|
||||
writel(1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK |
|
||||
armpll_clk_tab[i].ndiv_int <<
|
||||
IHOST_PROC_CLK_PLLARMA__PLLARM_NDIV_INT_R |
|
||||
armpll_clk_tab[i].pdiv <<
|
||||
IHOST_PROC_CLK_PLLARMA__PLLARM_PDIV_R |
|
||||
1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_RESETB,
|
||||
IHOST_PROC_CLK_PLLARMA);
|
||||
|
||||
/* Poll ARM PLL Lock until operation complete */
|
||||
timeout_countdown = 0x100000;
|
||||
while (readl(IHOST_PROC_CLK_PLLARMA) &
|
||||
(1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK)) {
|
||||
timeout_countdown--;
|
||||
if (timeout_countdown == 0) {
|
||||
printf("ARM PLL lock failed\n");
|
||||
status = 1;
|
||||
goto armpll_config_done;
|
||||
}
|
||||
}
|
||||
|
||||
pll = readl(IHOST_PROC_CLK_PLLARMA);
|
||||
pll |= (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_POST_RESETB);
|
||||
writel(pll, IHOST_PROC_CLK_PLLARMA);
|
||||
|
||||
/* Set the policy */
|
||||
writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE |
|
||||
armpll_clk_tab[i].freqid <<
|
||||
IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R |
|
||||
armpll_clk_tab[i].freqid <<
|
||||
IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R |
|
||||
armpll_clk_tab[i].freqid <<
|
||||
IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R |
|
||||
armpll_clk_tab[i+4].freqid <<
|
||||
IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R,
|
||||
IHOST_PROC_CLK_POLICY_FREQ);
|
||||
|
||||
writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE0_CLKGATE);
|
||||
writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE1_CLKGATE);
|
||||
writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_SWITCH_CLKGATE);
|
||||
writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_PERIPH_CLKGATE);
|
||||
writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_APB0_CLKGATE);
|
||||
|
||||
writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO |
|
||||
1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC,
|
||||
IHOST_PROC_CLK_POLICY_CTL);
|
||||
|
||||
/* Poll CCU until operation complete */
|
||||
timeout_countdown = 0x100000;
|
||||
while (readl(IHOST_PROC_CLK_POLICY_CTL) &
|
||||
(1 << IHOST_PROC_CLK_POLICY_CTL__GO)) {
|
||||
timeout_countdown--;
|
||||
if (timeout_countdown == 0) {
|
||||
printf("CCU polling failed\n");
|
||||
status = 1;
|
||||
goto armpll_config_done;
|
||||
}
|
||||
}
|
||||
|
||||
status = 0;
|
||||
armpll_config_done:
|
||||
/* Disable access to PLL registers */
|
||||
writel(0, IHOST_PROC_CLK_WR_ACCESS);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
|
||||
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
|
||||
void enable_caches(void)
|
||||
{
|
||||
/* Enable D-cache. I-cache is already enabled in start.S */
|
||||
dcache_enable();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <div64.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/iproc-common/timer.h>
|
||||
#include <asm/iproc-common/sysmap.h>
|
||||
|
||||
static inline uint64_t timer_global_read(void)
|
||||
{
|
||||
uint64_t cur_tick;
|
||||
uint32_t count_h;
|
||||
uint32_t count_l;
|
||||
|
||||
do {
|
||||
count_h = readl(IPROC_PERIPH_GLB_TIM_REG_BASE +
|
||||
TIMER_GLB_HI_OFFSET);
|
||||
count_l = readl(IPROC_PERIPH_GLB_TIM_REG_BASE +
|
||||
TIMER_GLB_LOW_OFFSET);
|
||||
cur_tick = readl(IPROC_PERIPH_GLB_TIM_REG_BASE +
|
||||
TIMER_GLB_HI_OFFSET);
|
||||
} while (cur_tick != count_h);
|
||||
|
||||
return (cur_tick << 32) + count_l;
|
||||
}
|
||||
|
||||
void timer_global_init(void)
|
||||
{
|
||||
writel(0, IPROC_PERIPH_GLB_TIM_REG_BASE + TIMER_GLB_CTRL_OFFSET);
|
||||
writel(0, IPROC_PERIPH_GLB_TIM_REG_BASE + TIMER_GLB_LOW_OFFSET);
|
||||
writel(0, IPROC_PERIPH_GLB_TIM_REG_BASE + TIMER_GLB_HI_OFFSET);
|
||||
writel(TIMER_GLB_TIM_CTRL_TIM_EN,
|
||||
IPROC_PERIPH_GLB_TIM_REG_BASE + TIMER_GLB_CTRL_OFFSET);
|
||||
}
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
timer_global_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long get_timer(unsigned long base)
|
||||
{
|
||||
uint64_t count;
|
||||
uint64_t ret;
|
||||
uint64_t tim_clk;
|
||||
uint64_t periph_clk;
|
||||
|
||||
count = timer_global_read();
|
||||
|
||||
/* default arm clk is 1GHz, periph_clk=arm_clk/2, tick per msec */
|
||||
periph_clk = 500000;
|
||||
tim_clk = lldiv(periph_clk,
|
||||
(((readl(IPROC_PERIPH_GLB_TIM_REG_BASE +
|
||||
TIMER_GLB_CTRL_OFFSET) &
|
||||
TIMER_GLB_TIM_CTRL_PRESC_MASK) >> 8) + 1));
|
||||
|
||||
ret = lldiv(count, (uint32_t)tim_clk);
|
||||
|
||||
/* returns msec */
|
||||
return ret - base;
|
||||
}
|
||||
|
||||
void __udelay(unsigned long usec)
|
||||
{
|
||||
uint64_t cur_tick, end_tick;
|
||||
uint64_t tim_clk;
|
||||
uint64_t periph_clk;
|
||||
|
||||
/* default arm clk is 1GHz, periph_clk=arm_clk/2, tick per usec */
|
||||
periph_clk = 500;
|
||||
|
||||
tim_clk = lldiv(periph_clk,
|
||||
(((readl(IPROC_PERIPH_GLB_TIM_REG_BASE +
|
||||
TIMER_GLB_CTRL_OFFSET) &
|
||||
TIMER_GLB_TIM_CTRL_PRESC_MASK) >> 8) + 1));
|
||||
|
||||
cur_tick = timer_global_read();
|
||||
|
||||
end_tick = tim_clk;
|
||||
end_tick *= usec;
|
||||
end_tick += cur_tick;
|
||||
|
||||
do {
|
||||
cur_tick = timer_global_read();
|
||||
|
||||
} while (cur_tick < end_tick);
|
||||
}
|
||||
|
||||
void timer_systick_init(uint32_t tick_ms)
|
||||
{
|
||||
/* Disable timer and clear interrupt status*/
|
||||
writel(0, IPROC_PERIPH_PVT_TIM_REG_BASE + TIMER_PVT_CTRL_OFFSET);
|
||||
writel(TIMER_PVT_TIM_INT_STATUS_SET,
|
||||
IPROC_PERIPH_PVT_TIM_REG_BASE + TIMER_PVT_STATUS_OFFSET);
|
||||
writel((PLL_AXI_CLK/1000) * tick_ms,
|
||||
IPROC_PERIPH_PVT_TIM_REG_BASE + TIMER_PVT_LOAD_OFFSET);
|
||||
writel(TIMER_PVT_TIM_CTRL_INT_EN |
|
||||
TIMER_PVT_TIM_CTRL_AUTO_RELD |
|
||||
TIMER_PVT_TIM_CTRL_TIM_EN,
|
||||
IPROC_PERIPH_PVT_TIM_REG_BASE + TIMER_PVT_CTRL_OFFSET);
|
||||
}
|
||||
|
||||
void timer_systick_isr(void *data)
|
||||
{
|
||||
writel(TIMER_PVT_TIM_INT_STATUS_SET,
|
||||
IPROC_PERIPH_PVT_TIM_REG_BASE + TIMER_PVT_STATUS_OFFSET);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (read timebase as long long).
|
||||
* On ARM it just returns the timer value in msec.
|
||||
*/
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
return get_timer(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is used in conjuction with get_ticks, which returns msec as ticks.
|
||||
* Here we just return ticks/sec = msec/sec = 1000
|
||||
*/
|
||||
ulong get_tbclk(void)
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2013 Broadcom Corporation.
|
||||
|
||||
obj-y += s_init.o
|
||||
obj-y += hwinit-common.o
|
||||
obj-y += clk-stubs.o
|
||||
obj-${CONFIG_KONA_RESET_S} += reset.o
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
/*
|
||||
* These weak functions are available to kona architectures that don't
|
||||
* require clock enables from the driver code.
|
||||
*/
|
||||
int __weak clk_sdio_enable(void *base, u32 rate, u32 *actual_ratep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak clk_bsc_enable(void *base, u32 rate, u32 *actual_ratep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak clk_usb_otg_enable(void *base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
|
||||
void enable_caches(void)
|
||||
{
|
||||
/* Enable D-cache. I-cache is already enabled in start.S */
|
||||
dcache_enable();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
.globl reset_cpu
|
||||
reset_cpu:
|
||||
ldr r1, =0x35001f00
|
||||
ldr r2, [r1]
|
||||
ldr r4, =0x80000000
|
||||
and r4, r2, r4
|
||||
ldr r3, =0xA5A500
|
||||
orr r4, r4, r3
|
||||
orr r4, r4, #0x1
|
||||
|
||||
str r4, [r1]
|
||||
|
||||
ldr r1, =0x35001f04
|
||||
ldr r2, [r1]
|
||||
ldr r4, =0x80000000
|
||||
and r4, r2, r4
|
||||
str r4, [r1]
|
||||
|
||||
_loop_forever:
|
||||
b _loop_forever
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Early system init. Currently empty.
|
||||
*/
|
||||
void s_init(void)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* A lowlevel_init function that sets up the stack to call a C function to
|
||||
* perform further init.
|
||||
*
|
||||
* (C) Copyright 2010
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Author :
|
||||
* Aneesh V <aneesh@ti.com>
|
||||
*/
|
||||
|
||||
#include <asm-offsets.h>
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
.pushsection .text.s_init, "ax"
|
||||
WEAK(s_init)
|
||||
bx lr
|
||||
ENDPROC(s_init)
|
||||
.popsection
|
||||
|
||||
.pushsection .text.lowlevel_init, "ax"
|
||||
WEAK(lowlevel_init)
|
||||
/*
|
||||
* Setup a temporary stack. Global data is not available yet.
|
||||
*/
|
||||
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
|
||||
ldr sp, =CONFIG_SPL_STACK
|
||||
#else
|
||||
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
|
||||
#endif
|
||||
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
|
||||
#ifdef CONFIG_SPL_DM
|
||||
mov r9, #0
|
||||
#else
|
||||
/*
|
||||
* Set up global data for boards that still need it. This will be
|
||||
* removed soon.
|
||||
*/
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
ldr r9, =gdata
|
||||
#else
|
||||
sub sp, sp, #GD_SIZE
|
||||
bic sp, sp, #7
|
||||
mov r9, sp
|
||||
#endif
|
||||
#endif
|
||||
/*
|
||||
* Save the old lr(passed in ip) and the current lr to stack
|
||||
*/
|
||||
push {ip, lr}
|
||||
|
||||
/*
|
||||
* Call the very early init function. This should do only the
|
||||
* absolute bare minimum to get started. It should not:
|
||||
*
|
||||
* - set up DRAM
|
||||
* - use global_data
|
||||
* - clear BSS
|
||||
* - try to start a console
|
||||
*
|
||||
* For boards with SPL this should be empty since SPL can do all of
|
||||
* this init in the SPL board_init_f() function which is called
|
||||
* immediately after this.
|
||||
*/
|
||||
bl s_init
|
||||
pop {ip, pc}
|
||||
ENDPROC(lowlevel_init)
|
||||
.popsection
|
||||
@@ -0,0 +1,115 @@
|
||||
config ARCH_LS1021A
|
||||
bool
|
||||
select SYS_FSL_DDR_BE if SYS_FSL_DDR
|
||||
select SYS_FSL_DDR_VER_50 if SYS_FSL_DDR
|
||||
select SYS_FSL_ERRATUM_A008378
|
||||
select SYS_FSL_ERRATUM_A008407
|
||||
select SYS_FSL_ERRATUM_A008850
|
||||
select SYS_FSL_ERRATUM_A008997
|
||||
select SYS_FSL_ERRATUM_A009007
|
||||
select SYS_FSL_ERRATUM_A009008
|
||||
select SYS_FSL_ERRATUM_A009663
|
||||
select SYS_FSL_ERRATUM_A009798
|
||||
select SYS_FSL_ERRATUM_A009942
|
||||
select SYS_FSL_ERRATUM_A010315
|
||||
select SYS_FSL_HAS_CCI400
|
||||
select SYS_FSL_HAS_DDR3 if SYS_FSL_DDR
|
||||
select SYS_FSL_HAS_DDR4 if SYS_FSL_DDR
|
||||
select SYS_FSL_HAS_SEC
|
||||
select SYS_FSL_SEC_COMPAT_5
|
||||
select SYS_FSL_SEC_LE
|
||||
select SYS_FSL_SRDS_1
|
||||
select SYS_HAS_SERDES
|
||||
imply CMD_PCI
|
||||
imply SCSI
|
||||
imply SCSI_AHCI
|
||||
|
||||
menu "LS102xA architecture"
|
||||
depends on ARCH_LS1021A
|
||||
|
||||
config FSL_PCIE_COMPAT
|
||||
string "PCIe compatible of Kernel DT"
|
||||
depends on PCIE_LAYERSCAPE
|
||||
default "fsl,ls1021a-pcie" if ARCH_LS1021A
|
||||
help
|
||||
This compatible is used to find pci controller node in Kernel DT
|
||||
to complete fixup.
|
||||
|
||||
config LS1_DEEP_SLEEP
|
||||
bool "Deep sleep"
|
||||
depends on ARCH_LS1021A
|
||||
|
||||
config MAX_CPUS
|
||||
int "Maximum number of CPUs permitted for LS102xA"
|
||||
depends on ARCH_LS1021A
|
||||
default 2
|
||||
help
|
||||
Set this number to the maximum number of possible CPUs in the SoC.
|
||||
SoCs may have multiple clusters with each cluster may have multiple
|
||||
ports. If some ports are reserved but higher ports are used for
|
||||
cores, count the reserved ports. This will allocate enough memory
|
||||
in spin table to properly handle all cores.
|
||||
|
||||
config NXP_ESBC
|
||||
bool "NXP_ESBC"
|
||||
help
|
||||
Enable Freescale Secure Boot feature. Normally selected
|
||||
by defconfig. If unsure, do not change.
|
||||
|
||||
config SYS_CCI400_OFFSET
|
||||
hex "Offset for CCI400 base"
|
||||
depends on SYS_FSL_HAS_CCI400
|
||||
default 0x180000
|
||||
help
|
||||
Offset for CCI400 base.
|
||||
CCI400 base addr = CCSRBAR + CCI400_OFFSET
|
||||
|
||||
config SYS_FSL_ERRATUM_A008850
|
||||
bool
|
||||
help
|
||||
Workaround for DDR erratum A008850
|
||||
|
||||
config SYS_FSL_ERRATUM_A008997
|
||||
bool
|
||||
help
|
||||
Workaround for USB PHY erratum A008997
|
||||
|
||||
config SYS_FSL_ERRATUM_A009007
|
||||
bool
|
||||
help
|
||||
Workaround for USB PHY erratum A009007
|
||||
|
||||
config SYS_FSL_ERRATUM_A009008
|
||||
bool
|
||||
help
|
||||
Workaround for USB PHY erratum A009008
|
||||
|
||||
config SYS_FSL_ERRATUM_A009798
|
||||
bool
|
||||
help
|
||||
Workaround for USB PHY erratum A009798
|
||||
|
||||
config SYS_FSL_ERRATUM_A010315
|
||||
bool "Workaround for PCIe erratum A010315"
|
||||
|
||||
config SYS_FSL_HAS_CCI400
|
||||
bool
|
||||
|
||||
config SYS_FSL_SRDS_1
|
||||
bool
|
||||
|
||||
config SYS_FSL_SRDS_2
|
||||
bool
|
||||
|
||||
config SYS_HAS_SERDES
|
||||
bool
|
||||
|
||||
config SYS_FSL_IFC_BANK_COUNT
|
||||
int "Maximum banks of Integrated flash controller"
|
||||
depends on ARCH_LS1021A
|
||||
default 8
|
||||
|
||||
config SYS_FSL_ERRATUM_A008407
|
||||
bool
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright 2014 Freescale Semiconductor, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y += cpu.o
|
||||
obj-y += clock.o
|
||||
obj-y += timer.o
|
||||
obj-y += fsl_epu.o
|
||||
obj-y += soc.o
|
||||
|
||||
obj-$(CONFIG_OF_LIBFDT) += fdt.o
|
||||
obj-$(CONFIG_SYS_HAS_SERDES) += fsl_ls1_serdes.o ls102xa_serdes.o
|
||||
obj-$(CONFIG_SPL) += spl.o
|
||||
|
||||
ifdef CONFIG_ARMV7_PSCI
|
||||
obj-y += psci.o ls102xa_psci.o
|
||||
endif
|
||||
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <fsl_ifc.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#ifndef CONFIG_SYS_FSL_NUM_CC_PLLS
|
||||
#define CONFIG_SYS_FSL_NUM_CC_PLLS 2
|
||||
#endif
|
||||
|
||||
void get_sys_info(struct sys_info *sys_info)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
struct ccsr_clk *clk = (void *)(CONFIG_SYS_FSL_LS1_CLK_ADDR);
|
||||
unsigned int cpu;
|
||||
const u8 core_cplx_pll[6] = {
|
||||
[0] = 0, /* CC1 PPL / 1 */
|
||||
[1] = 0, /* CC1 PPL / 2 */
|
||||
[4] = 1, /* CC2 PPL / 1 */
|
||||
[5] = 1, /* CC2 PPL / 2 */
|
||||
};
|
||||
|
||||
const u8 core_cplx_pll_div[6] = {
|
||||
[0] = 1, /* CC1 PPL / 1 */
|
||||
[1] = 2, /* CC1 PPL / 2 */
|
||||
[4] = 1, /* CC2 PPL / 1 */
|
||||
[5] = 2, /* CC2 PPL / 2 */
|
||||
};
|
||||
|
||||
uint i;
|
||||
uint freq_c_pll[CONFIG_SYS_FSL_NUM_CC_PLLS];
|
||||
uint ratio[CONFIG_SYS_FSL_NUM_CC_PLLS];
|
||||
unsigned long sysclk = CONFIG_SYS_CLK_FREQ;
|
||||
|
||||
sys_info->freq_systembus = sysclk;
|
||||
#ifdef CONFIG_DDR_CLK_FREQ
|
||||
sys_info->freq_ddrbus = CONFIG_DDR_CLK_FREQ;
|
||||
#else
|
||||
sys_info->freq_ddrbus = sysclk;
|
||||
#endif
|
||||
|
||||
sys_info->freq_systembus *= (in_be32(&gur->rcwsr[0]) >>
|
||||
RCWSR0_SYS_PLL_RAT_SHIFT) & RCWSR0_SYS_PLL_RAT_MASK;
|
||||
sys_info->freq_ddrbus *= (in_be32(&gur->rcwsr[0]) >>
|
||||
RCWSR0_MEM_PLL_RAT_SHIFT) & RCWSR0_MEM_PLL_RAT_MASK;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_FSL_NUM_CC_PLLS; i++) {
|
||||
ratio[i] = (in_be32(&clk->pllcgsr[i].pllcngsr) >> 1) & 0x3f;
|
||||
if (ratio[i] > 4)
|
||||
freq_c_pll[i] = sysclk * ratio[i];
|
||||
else
|
||||
freq_c_pll[i] = sys_info->freq_systembus * ratio[i];
|
||||
}
|
||||
|
||||
for (cpu = 0; cpu < CONFIG_MAX_CPUS; cpu++) {
|
||||
u32 c_pll_sel = (in_be32(&clk->clkcsr[cpu].clkcncsr) >> 27)
|
||||
& 0xf;
|
||||
u32 cplx_pll = core_cplx_pll[c_pll_sel];
|
||||
|
||||
sys_info->freq_processor[cpu] =
|
||||
freq_c_pll[cplx_pll] / core_cplx_pll_div[c_pll_sel];
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FSL_IFC)
|
||||
sys_info->freq_localbus = sys_info->freq_systembus;
|
||||
#endif
|
||||
}
|
||||
|
||||
int get_clocks(void)
|
||||
{
|
||||
struct sys_info sys_info;
|
||||
|
||||
get_sys_info(&sys_info);
|
||||
gd->cpu_clk = sys_info.freq_processor[0];
|
||||
gd->bus_clk = sys_info.freq_systembus;
|
||||
gd->mem_clk = sys_info.freq_ddrbus * 2;
|
||||
|
||||
#if defined(CONFIG_FSL_ESDHC)
|
||||
gd->arch.sdhc_clk = gd->bus_clk;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong get_bus_freq(ulong dummy)
|
||||
{
|
||||
return gd->bus_clk;
|
||||
}
|
||||
|
||||
ulong get_ddr_freq(ulong dummy)
|
||||
{
|
||||
return gd->mem_clk;
|
||||
}
|
||||
|
||||
int get_serial_clock(void)
|
||||
{
|
||||
return gd->bus_clk / 2;
|
||||
}
|
||||
|
||||
unsigned int mxc_get_clock(enum mxc_clock clk)
|
||||
{
|
||||
switch (clk) {
|
||||
case MXC_I2C_CLK:
|
||||
return get_bus_freq(0) / 2;
|
||||
case MXC_DSPI_CLK:
|
||||
return get_bus_freq(0) / 2;
|
||||
case MXC_UART_CLK:
|
||||
return get_bus_freq(0) / 2;
|
||||
default:
|
||||
printf("Unsupported clock\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <vsprintf.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/system.h>
|
||||
#include <tsec.h>
|
||||
#include <netdev.h>
|
||||
#include <fsl_esdhc.h>
|
||||
#include <config.h>
|
||||
#include <fsl_wdog.h>
|
||||
|
||||
#include "fsl_epu.h"
|
||||
|
||||
#define DCSR_RCPM2_BLOCK_OFFSET 0x223000
|
||||
#define DCSR_RCPM2_CPMFSMCR0 0x400
|
||||
#define DCSR_RCPM2_CPMFSMSR0 0x404
|
||||
#define DCSR_RCPM2_CPMFSMCR1 0x414
|
||||
#define DCSR_RCPM2_CPMFSMSR1 0x418
|
||||
#define CPMFSMSR_FSM_STATE_MASK 0x7f
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
|
||||
|
||||
/*
|
||||
* Bit[1] of the descriptor indicates the descriptor type,
|
||||
* and bit[0] indicates whether the descriptor is valid.
|
||||
*/
|
||||
#define PMD_TYPE_TABLE 0x3
|
||||
#define PMD_TYPE_SECT 0x1
|
||||
|
||||
/* AttrIndx[2:0] */
|
||||
#define PMD_ATTRINDX(t) ((t) << 2)
|
||||
|
||||
/* Section */
|
||||
#define PMD_SECT_AF (1 << 10)
|
||||
|
||||
#define BLOCK_SIZE_L1 (1UL << 30)
|
||||
#define BLOCK_SIZE_L2 (1UL << 21)
|
||||
|
||||
/* TTBCR flags */
|
||||
#define TTBCR_EAE (1 << 31)
|
||||
#define TTBCR_T0SZ(x) ((x) << 0)
|
||||
#define TTBCR_T1SZ(x) ((x) << 16)
|
||||
#define TTBCR_USING_TTBR0 (TTBCR_T0SZ(0) | TTBCR_T1SZ(0))
|
||||
#define TTBCR_IRGN0_NC (0 << 8)
|
||||
#define TTBCR_IRGN0_WBWA (1 << 8)
|
||||
#define TTBCR_IRGN0_WT (2 << 8)
|
||||
#define TTBCR_IRGN0_WBNWA (3 << 8)
|
||||
#define TTBCR_IRGN0_MASK (3 << 8)
|
||||
#define TTBCR_ORGN0_NC (0 << 10)
|
||||
#define TTBCR_ORGN0_WBWA (1 << 10)
|
||||
#define TTBCR_ORGN0_WT (2 << 10)
|
||||
#define TTBCR_ORGN0_WBNWA (3 << 10)
|
||||
#define TTBCR_ORGN0_MASK (3 << 10)
|
||||
#define TTBCR_SHARED_NON (0 << 12)
|
||||
#define TTBCR_SHARED_OUTER (2 << 12)
|
||||
#define TTBCR_SHARED_INNER (3 << 12)
|
||||
#define TTBCR_EPD0 (0 << 7)
|
||||
#define TTBCR (TTBCR_SHARED_NON | \
|
||||
TTBCR_ORGN0_NC | \
|
||||
TTBCR_IRGN0_NC | \
|
||||
TTBCR_USING_TTBR0 | \
|
||||
TTBCR_EAE)
|
||||
|
||||
/*
|
||||
* Memory region attributes for LPAE (defined in pgtable):
|
||||
*
|
||||
* n = AttrIndx[2:0]
|
||||
*
|
||||
* n MAIR
|
||||
* UNCACHED 000 00000000
|
||||
* BUFFERABLE 001 01000100
|
||||
* DEV_WC 001 01000100
|
||||
* WRITETHROUGH 010 10101010
|
||||
* WRITEBACK 011 11101110
|
||||
* DEV_CACHED 011 11101110
|
||||
* DEV_SHARED 100 00000100
|
||||
* DEV_NONSHARED 100 00000100
|
||||
* unused 101
|
||||
* unused 110
|
||||
* WRITEALLOC 111 11111111
|
||||
*/
|
||||
#define MT_MAIR0 0xeeaa4400
|
||||
#define MT_MAIR1 0xff000004
|
||||
#define MT_STRONLY_ORDER 0
|
||||
#define MT_NORMAL_NC 1
|
||||
#define MT_DEVICE_MEM 4
|
||||
#define MT_NORMAL 7
|
||||
|
||||
/* The phy_addr must be aligned to 4KB */
|
||||
static inline void set_pgtable(u32 *page_table, u32 index, u32 phy_addr)
|
||||
{
|
||||
u32 value = phy_addr | PMD_TYPE_TABLE;
|
||||
|
||||
page_table[2 * index] = value;
|
||||
page_table[2 * index + 1] = 0;
|
||||
}
|
||||
|
||||
/* The phy_addr must be aligned to 4KB */
|
||||
static inline void set_pgsection(u32 *page_table, u32 index, u64 phy_addr,
|
||||
u32 memory_type)
|
||||
{
|
||||
u64 value;
|
||||
|
||||
value = phy_addr | PMD_TYPE_SECT | PMD_SECT_AF;
|
||||
value |= PMD_ATTRINDX(memory_type);
|
||||
page_table[2 * index] = value & 0xFFFFFFFF;
|
||||
page_table[2 * index + 1] = (value >> 32) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start MMU after DDR is available, we create MMU table in DRAM.
|
||||
* The base address of TTLB is gd->arch.tlb_addr. We use two
|
||||
* levels of translation tables here to cover 40-bit address space.
|
||||
*
|
||||
* The TTLBs are located at PHY 2G~4G.
|
||||
*
|
||||
* VA mapping:
|
||||
*
|
||||
* ------- <---- 0GB
|
||||
* | |
|
||||
* | |
|
||||
* |-------| <---- 0x24000000
|
||||
* |///////| ===> 192MB VA map for PCIe1 with offset 0x40_0000_0000
|
||||
* |-------| <---- 0x300000000
|
||||
* | |
|
||||
* |-------| <---- 0x34000000
|
||||
* |///////| ===> 192MB VA map for PCIe2 with offset 0x48_0000_0000
|
||||
* |-------| <---- 0x40000000
|
||||
* | |
|
||||
* |-------| <---- 0x80000000 DDR0 space start
|
||||
* |\\\\\\\|
|
||||
*.|\\\\\\\| ===> 2GB VA map for 2GB DDR0 Memory space
|
||||
* |\\\\\\\|
|
||||
* ------- <---- 4GB DDR0 space end
|
||||
*/
|
||||
static void mmu_setup(void)
|
||||
{
|
||||
u32 *level0_table = (u32 *)gd->arch.tlb_addr;
|
||||
u32 *level1_table = (u32 *)(gd->arch.tlb_addr + 0x1000);
|
||||
u64 va_start = 0;
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
/* Level 0 Table 2-3 are used to map DDR */
|
||||
set_pgsection(level0_table, 3, 3 * BLOCK_SIZE_L1, MT_NORMAL);
|
||||
set_pgsection(level0_table, 2, 2 * BLOCK_SIZE_L1, MT_NORMAL);
|
||||
/* Level 0 Table 1 is used to map device */
|
||||
set_pgsection(level0_table, 1, 1 * BLOCK_SIZE_L1, MT_DEVICE_MEM);
|
||||
/* Level 0 Table 0 is used to map device including PCIe MEM */
|
||||
set_pgtable(level0_table, 0, (u32)level1_table);
|
||||
|
||||
/* Level 1 has 512 entries */
|
||||
for (i = 0; i < 512; i++) {
|
||||
/* Mapping for PCIe 1 */
|
||||
if (va_start >= CONFIG_SYS_PCIE1_VIRT_ADDR &&
|
||||
va_start < (CONFIG_SYS_PCIE1_VIRT_ADDR +
|
||||
CONFIG_SYS_PCIE_MMAP_SIZE))
|
||||
set_pgsection(level1_table, i,
|
||||
CONFIG_SYS_PCIE1_PHYS_BASE + va_start,
|
||||
MT_DEVICE_MEM);
|
||||
/* Mapping for PCIe 2 */
|
||||
else if (va_start >= CONFIG_SYS_PCIE2_VIRT_ADDR &&
|
||||
va_start < (CONFIG_SYS_PCIE2_VIRT_ADDR +
|
||||
CONFIG_SYS_PCIE_MMAP_SIZE))
|
||||
set_pgsection(level1_table, i,
|
||||
CONFIG_SYS_PCIE2_PHYS_BASE + va_start,
|
||||
MT_DEVICE_MEM);
|
||||
else
|
||||
set_pgsection(level1_table, i,
|
||||
va_start,
|
||||
MT_DEVICE_MEM);
|
||||
va_start += BLOCK_SIZE_L2;
|
||||
}
|
||||
|
||||
asm volatile("dsb sy;isb");
|
||||
asm volatile("mcr p15, 0, %0, c2, c0, 2" /* Write RT to TTBCR */
|
||||
: : "r" (TTBCR) : "memory");
|
||||
asm volatile("mcrr p15, 0, %0, %1, c2" /* TTBR 0 */
|
||||
: : "r" ((u32)level0_table), "r" (0) : "memory");
|
||||
asm volatile("mcr p15, 0, %0, c10, c2, 0" /* write MAIR 0 */
|
||||
: : "r" (MT_MAIR0) : "memory");
|
||||
asm volatile("mcr p15, 0, %0, c10, c2, 1" /* write MAIR 1 */
|
||||
: : "r" (MT_MAIR1) : "memory");
|
||||
|
||||
/* Set the access control to all-supervisor */
|
||||
asm volatile("mcr p15, 0, %0, c3, c0, 0"
|
||||
: : "r" (~0));
|
||||
|
||||
/* Enable the mmu */
|
||||
reg = get_cr();
|
||||
set_cr(reg | CR_M);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from lib/board.c. It recreates MMU
|
||||
* table in main memory. MMU and i/d-cache are enabled here.
|
||||
*/
|
||||
void enable_caches(void)
|
||||
{
|
||||
/* Invalidate all TLB */
|
||||
mmu_page_table_flush(gd->arch.tlb_addr,
|
||||
gd->arch.tlb_addr + gd->arch.tlb_size);
|
||||
/* Set up and enable mmu */
|
||||
mmu_setup();
|
||||
|
||||
/* Invalidate & Enable d-cache */
|
||||
invalidate_dcache_all();
|
||||
set_cr(get_cr() | CR_C);
|
||||
}
|
||||
#endif /* #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) */
|
||||
|
||||
|
||||
uint get_svr(void)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
|
||||
return in_be32(&gur->svr);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DISPLAY_CPUINFO)
|
||||
int print_cpuinfo(void)
|
||||
{
|
||||
char buf1[32], buf2[32];
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
unsigned int svr, major, minor, ver, i;
|
||||
|
||||
svr = in_be32(&gur->svr);
|
||||
major = SVR_MAJ(svr);
|
||||
minor = SVR_MIN(svr);
|
||||
|
||||
puts("CPU: Freescale LayerScape ");
|
||||
|
||||
ver = SVR_SOC_VER(svr);
|
||||
switch (ver) {
|
||||
case SOC_VER_SLS1020:
|
||||
puts("SLS1020");
|
||||
break;
|
||||
case SOC_VER_LS1020:
|
||||
puts("LS1020");
|
||||
break;
|
||||
case SOC_VER_LS1021:
|
||||
puts("LS1021");
|
||||
break;
|
||||
case SOC_VER_LS1022:
|
||||
puts("LS1022");
|
||||
break;
|
||||
default:
|
||||
puts("Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_E_PROCESSOR(svr) && (ver != SOC_VER_SLS1020))
|
||||
puts("E");
|
||||
|
||||
printf(", Version: %d.%d, (0x%08x)\n", major, minor, svr);
|
||||
|
||||
puts("Clock Configuration:");
|
||||
|
||||
printf("\n CPU0(ARMV7):%-4s MHz, ", strmhz(buf1, gd->cpu_clk));
|
||||
printf("\n Bus:%-4s MHz, ", strmhz(buf1, gd->bus_clk));
|
||||
printf("DDR:%-4s MHz (%s MT/s data rate), ",
|
||||
strmhz(buf1, gd->mem_clk/2), strmhz(buf2, gd->mem_clk));
|
||||
puts("\n");
|
||||
|
||||
/* Display the RCW, so that no one gets confused as to what RCW
|
||||
* we're actually using for this boot.
|
||||
*/
|
||||
puts("Reset Configuration Word (RCW):");
|
||||
for (i = 0; i < ARRAY_SIZE(gur->rcwsr); i++) {
|
||||
u32 rcw = in_be32(&gur->rcwsr[i]);
|
||||
|
||||
if ((i % 4) == 0)
|
||||
printf("\n %08x:", i * 4);
|
||||
printf(" %08x", rcw);
|
||||
}
|
||||
puts("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSL_ESDHC
|
||||
int cpu_mmc_init(bd_t *bis)
|
||||
{
|
||||
return fsl_esdhc_mmc_init(bis);
|
||||
}
|
||||
#endif
|
||||
|
||||
int cpu_eth_init(bd_t *bis)
|
||||
{
|
||||
#if defined(CONFIG_TSEC_ENET) && !defined(CONFIG_DM_ETH)
|
||||
tsec_standard_init(bis);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arch_cpu_init(void)
|
||||
{
|
||||
void *epu_base = (void *)(CONFIG_SYS_DCSRBAR + EPU_BLOCK_OFFSET);
|
||||
void *rcpm2_base =
|
||||
(void *)(CONFIG_SYS_DCSRBAR + DCSR_RCPM2_BLOCK_OFFSET);
|
||||
struct ccsr_scfg *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
u32 state;
|
||||
|
||||
/*
|
||||
* The RCPM FSM state may not be reset after power-on.
|
||||
* So, reset them.
|
||||
*/
|
||||
state = in_be32(rcpm2_base + DCSR_RCPM2_CPMFSMSR0) &
|
||||
CPMFSMSR_FSM_STATE_MASK;
|
||||
if (state != 0) {
|
||||
out_be32(rcpm2_base + DCSR_RCPM2_CPMFSMCR0, 0x80);
|
||||
out_be32(rcpm2_base + DCSR_RCPM2_CPMFSMCR0, 0x0);
|
||||
}
|
||||
|
||||
state = in_be32(rcpm2_base + DCSR_RCPM2_CPMFSMSR1) &
|
||||
CPMFSMSR_FSM_STATE_MASK;
|
||||
if (state != 0) {
|
||||
out_be32(rcpm2_base + DCSR_RCPM2_CPMFSMCR1, 0x80);
|
||||
out_be32(rcpm2_base + DCSR_RCPM2_CPMFSMCR1, 0x0);
|
||||
}
|
||||
|
||||
/*
|
||||
* After wakeup from deep sleep, Clear EPU registers
|
||||
* as early as possible to prevent from possible issue.
|
||||
* It's also safe to clear at normal boot.
|
||||
*/
|
||||
fsl_epu_clean(epu_base);
|
||||
|
||||
setbits_be32(&scfg->snpcnfgcr, SCFG_SNPCNFGCR_SEC_RD_WR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARMV7_NONSEC
|
||||
/* Set the address at which the secondary core starts from.*/
|
||||
void smp_set_core_boot_addr(unsigned long addr, int corenr)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
|
||||
out_be32(&gur->scratchrw[0], addr);
|
||||
}
|
||||
|
||||
/* Release the secondary core from holdoff state and kick it */
|
||||
void smp_kick_all_cpus(void)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
|
||||
out_be32(&gur->brrl, 0x2);
|
||||
|
||||
/*
|
||||
* LS1 STANDBYWFE is not captured outside the ARM module in the soc.
|
||||
* So add a delay to wait bootrom execute WFE.
|
||||
*/
|
||||
udelay(1);
|
||||
|
||||
asm volatile("sev");
|
||||
}
|
||||
#endif
|
||||
|
||||
void reset_cpu(ulong addr)
|
||||
{
|
||||
struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
|
||||
|
||||
clrbits_be16(&wdog->wcr, WCR_SRS);
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* Let the watchdog trigger
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void arch_preboot_os(void)
|
||||
{
|
||||
unsigned long ctrl;
|
||||
|
||||
/* Disable PL1 Physical Timer */
|
||||
asm("mrc p15, 0, %0, c14, c2, 1" : "=r" (ctrl));
|
||||
ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
|
||||
asm("mcr p15, 0, %0, c14, c2, 1" : : "r" (ctrl));
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <linux/ctype.h>
|
||||
#ifdef CONFIG_FSL_ESDHC
|
||||
#include <fsl_esdhc.h>
|
||||
#endif
|
||||
#include <tsec.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <fsl_sec.h>
|
||||
#include <dm.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
void ft_fixup_enet_phy_connect_type(void *fdt)
|
||||
{
|
||||
#ifdef CONFIG_DM_ETH
|
||||
struct udevice *dev;
|
||||
#else
|
||||
struct eth_device *dev;
|
||||
#endif
|
||||
struct tsec_private *priv;
|
||||
const char *enet_path, *phy_path;
|
||||
char enet[16];
|
||||
char phy[16];
|
||||
int phy_node;
|
||||
int i = 0;
|
||||
uint32_t ph;
|
||||
#ifdef CONFIG_DM_ETH
|
||||
char *name[3] = { "ethernet@2d10000", "ethernet@2d50000",
|
||||
"ethernet@2d90000" };
|
||||
#else
|
||||
char *name[3] = { "eTSEC1", "eTSEC2", "eTSEC3" };
|
||||
#endif
|
||||
|
||||
for (; i < ARRAY_SIZE(name); i++) {
|
||||
dev = eth_get_dev_by_name(name[i]);
|
||||
if (dev) {
|
||||
sprintf(enet, "ethernet%d", i);
|
||||
sprintf(phy, "enet%d_rgmii_phy", i);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
priv = dev->priv;
|
||||
if (priv->flags & TSEC_SGMII)
|
||||
continue;
|
||||
|
||||
enet_path = fdt_get_alias(fdt, enet);
|
||||
if (!enet_path)
|
||||
continue;
|
||||
|
||||
phy_path = fdt_get_alias(fdt, phy);
|
||||
if (!phy_path)
|
||||
continue;
|
||||
|
||||
phy_node = fdt_path_offset(fdt, phy_path);
|
||||
if (phy_node < 0)
|
||||
continue;
|
||||
|
||||
ph = fdt_create_phandle(fdt, phy_node);
|
||||
if (ph)
|
||||
do_fixup_by_path_u32(fdt, enet_path,
|
||||
"phy-handle", ph, 1);
|
||||
|
||||
do_fixup_by_path(fdt, enet_path, "phy-connection-type",
|
||||
phy_string_for_interface(
|
||||
PHY_INTERFACE_MODE_RGMII_ID),
|
||||
strlen(phy_string_for_interface(
|
||||
PHY_INTERFACE_MODE_RGMII_ID)) + 1,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
void ft_cpu_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
int off;
|
||||
int val;
|
||||
const char *sysclk_path;
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
unsigned int svr;
|
||||
svr = in_be32(&gur->svr);
|
||||
|
||||
unsigned long busclk = get_bus_freq(0);
|
||||
|
||||
/* delete crypto node if not on an E-processor */
|
||||
if (!IS_E_PROCESSOR(svr))
|
||||
fdt_fixup_crypto_node(blob, 0);
|
||||
#if CONFIG_SYS_FSL_SEC_COMPAT >= 4
|
||||
else {
|
||||
ccsr_sec_t __iomem *sec;
|
||||
|
||||
sec = (void __iomem *)CONFIG_SYS_FSL_SEC_ADDR;
|
||||
fdt_fixup_crypto_node(blob, sec_in32(&sec->secvid_ms));
|
||||
}
|
||||
#endif
|
||||
|
||||
off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4);
|
||||
while (off != -FDT_ERR_NOTFOUND) {
|
||||
val = gd->cpu_clk;
|
||||
fdt_setprop(blob, off, "clock-frequency", &val, 4);
|
||||
off = fdt_node_offset_by_prop_value(blob, off,
|
||||
"device_type", "cpu", 4);
|
||||
}
|
||||
|
||||
do_fixup_by_prop_u32(blob, "device_type", "soc",
|
||||
4, "bus-frequency", busclk, 1);
|
||||
|
||||
ft_fixup_enet_phy_connect_type(blob);
|
||||
|
||||
#ifdef CONFIG_SYS_NS16550
|
||||
do_fixup_by_compat_u32(blob, "fsl,16550-FIFO64",
|
||||
"clock-frequency", CONFIG_SYS_NS16550_CLK, 1);
|
||||
#endif
|
||||
|
||||
sysclk_path = fdt_get_alias(blob, "sysclk");
|
||||
if (sysclk_path)
|
||||
do_fixup_by_path_u32(blob, sysclk_path, "clock-frequency",
|
||||
CONFIG_SYS_CLK_FREQ, 1);
|
||||
do_fixup_by_compat_u32(blob, "fsl,qoriq-sysclk-2.0",
|
||||
"clock-frequency", CONFIG_SYS_CLK_FREQ, 1);
|
||||
|
||||
#if defined(CONFIG_DEEP_SLEEP) && defined(CONFIG_SD_BOOT)
|
||||
#define UBOOT_HEAD_LEN 0x1000
|
||||
/*
|
||||
* Reserved memory in SD boot deep sleep case.
|
||||
* Second stage uboot binary and malloc space should be reserved.
|
||||
* If the memory they occupied has not been reserved, then this
|
||||
* space would be used by kernel and overwritten in uboot when
|
||||
* deep sleep resume, which cause deep sleep failed.
|
||||
* Since second uboot binary has a head, that space need to be
|
||||
* reserved either(assuming its size is less than 0x1000).
|
||||
*/
|
||||
off = fdt_add_mem_rsv(blob, CONFIG_SYS_TEXT_BASE - UBOOT_HEAD_LEN,
|
||||
CONFIG_SYS_MONITOR_LEN + CONFIG_SYS_SPL_MALLOC_SIZE +
|
||||
UBOOT_HEAD_LEN);
|
||||
if (off < 0)
|
||||
printf("Failed to reserve memory for SD boot deep sleep: %s\n",
|
||||
fdt_strerror(off));
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FSL_ESDHC)
|
||||
fdt_fixup_esdhc(blob, bd);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* platform bus clock = system bus clock/2
|
||||
* Here busclk = system bus clock
|
||||
* We are using the platform bus clock as 1588 Timer reference
|
||||
* clock source select
|
||||
*/
|
||||
do_fixup_by_compat_u32(blob, "fsl, gianfar-ptp-timer",
|
||||
"timer-frequency", busclk / 2, 1);
|
||||
|
||||
/*
|
||||
* clock-freq should change to clock-frequency and
|
||||
* flexcan-v1.0 should change to p1010-flexcan respectively
|
||||
* in the future.
|
||||
*/
|
||||
do_fixup_by_compat_u32(blob, "fsl, flexcan-v1.0",
|
||||
"clock_freq", busclk / 2, 1);
|
||||
|
||||
do_fixup_by_compat_u32(blob, "fsl, flexcan-v1.0",
|
||||
"clock-frequency", busclk / 2, 1);
|
||||
|
||||
do_fixup_by_compat_u32(blob, "fsl, ls1021a-flexcan",
|
||||
"clock-frequency", busclk / 2, 1);
|
||||
|
||||
#if defined(CONFIG_QSPI_BOOT) || defined(CONFIG_SD_BOOT_QSPI)
|
||||
off = fdt_node_offset_by_compat_reg(blob, FSL_IFC_COMPAT,
|
||||
CONFIG_SYS_IFC_ADDR);
|
||||
fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
|
||||
#else
|
||||
off = fdt_node_offset_by_compat_reg(blob, FSL_QSPI_COMPAT,
|
||||
QSPI0_BASE_ADDR);
|
||||
fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
|
||||
off = fdt_node_offset_by_compat_reg(blob, FSL_DSPI_COMPAT,
|
||||
DSPI1_BASE_ADDR);
|
||||
fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "fsl_epu.h"
|
||||
|
||||
struct fsm_reg_vals epu_default_val[] = {
|
||||
/* EPGCR (Event Processor Global Control Register) */
|
||||
{EPGCR, 0},
|
||||
/* EPECR (Event Processor Event Control Registers) */
|
||||
{EPECR0 + EPECR_STRIDE * 0, 0},
|
||||
{EPECR0 + EPECR_STRIDE * 1, 0},
|
||||
{EPECR0 + EPECR_STRIDE * 2, 0xF0004004},
|
||||
{EPECR0 + EPECR_STRIDE * 3, 0x80000084},
|
||||
{EPECR0 + EPECR_STRIDE * 4, 0x20000084},
|
||||
{EPECR0 + EPECR_STRIDE * 5, 0x08000004},
|
||||
{EPECR0 + EPECR_STRIDE * 6, 0x80000084},
|
||||
{EPECR0 + EPECR_STRIDE * 7, 0x80000084},
|
||||
{EPECR0 + EPECR_STRIDE * 8, 0x60000084},
|
||||
{EPECR0 + EPECR_STRIDE * 9, 0x08000084},
|
||||
{EPECR0 + EPECR_STRIDE * 10, 0x42000084},
|
||||
{EPECR0 + EPECR_STRIDE * 11, 0x90000084},
|
||||
{EPECR0 + EPECR_STRIDE * 12, 0x80000084},
|
||||
{EPECR0 + EPECR_STRIDE * 13, 0x08000084},
|
||||
{EPECR0 + EPECR_STRIDE * 14, 0x02000084},
|
||||
{EPECR0 + EPECR_STRIDE * 15, 0x00000004},
|
||||
/*
|
||||
* EPEVTCR (Event Processor EVT Pin Control Registers)
|
||||
* SCU8 triger EVT2, and SCU11 triger EVT9
|
||||
*/
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 0, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 1, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 3, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 4, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 5, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 6, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 7, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 8, 0},
|
||||
{EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001},
|
||||
/* EPCMPR (Event Processor Counter Compare Registers) */
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 0, 0},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 1, 0},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 3, 0},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 6, 0},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 7, 0},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 13, 0},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF},
|
||||
{EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF},
|
||||
/* EPCCR (Event Processor Counter Control Registers) */
|
||||
{EPCCR0 + EPCCR_STRIDE * 0, 0},
|
||||
{EPCCR0 + EPCCR_STRIDE * 1, 0},
|
||||
{EPCCR0 + EPCCR_STRIDE * 2, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 3, 0},
|
||||
{EPCCR0 + EPCCR_STRIDE * 4, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 5, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 6, 0},
|
||||
{EPCCR0 + EPCCR_STRIDE * 7, 0},
|
||||
{EPCCR0 + EPCCR_STRIDE * 8, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 9, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 10, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 11, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 12, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 13, 0},
|
||||
{EPCCR0 + EPCCR_STRIDE * 14, 0x92840000},
|
||||
{EPCCR0 + EPCCR_STRIDE * 15, 0x92840000},
|
||||
/* EPSMCR (Event Processor SCU Mux Control Registers) */
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 0, 0},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 1, 0},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031},
|
||||
{EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000},
|
||||
/* EPACR (Event Processor Action Control Registers) */
|
||||
{EPACR0 + EPACR_STRIDE * 0, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 1, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 2, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 3, 0x00000080},
|
||||
{EPACR0 + EPACR_STRIDE * 4, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 5, 0x00000040},
|
||||
{EPACR0 + EPACR_STRIDE * 6, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 7, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 8, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 9, 0x0000001C},
|
||||
{EPACR0 + EPACR_STRIDE * 10, 0x00000020},
|
||||
{EPACR0 + EPACR_STRIDE * 11, 0},
|
||||
{EPACR0 + EPACR_STRIDE * 12, 0x00000003},
|
||||
{EPACR0 + EPACR_STRIDE * 13, 0x06000000},
|
||||
{EPACR0 + EPACR_STRIDE * 14, 0x04000000},
|
||||
{EPACR0 + EPACR_STRIDE * 15, 0x02000000},
|
||||
/* EPIMCR (Event Processor Input Mux Control Registers) */
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 0, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 1, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 2, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 3, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 6, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 7, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 8, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 9, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 10, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 11, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 13, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 14, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 15, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 17, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 18, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 19, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 21, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 23, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 24, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 25, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 26, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 27, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 29, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 30, 0},
|
||||
{EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000},
|
||||
/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
|
||||
{EPXTRIGCR, 0x0000FFDF},
|
||||
/* end */
|
||||
{FSM_END_FLAG, 0},
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_epu_setup - Setup EPU registers to default values
|
||||
*/
|
||||
void fsl_epu_setup(void *epu_base)
|
||||
{
|
||||
struct fsm_reg_vals *data = epu_default_val;
|
||||
|
||||
if (!epu_base || !data)
|
||||
return;
|
||||
|
||||
while (data->offset != FSM_END_FLAG) {
|
||||
out_be32(epu_base + data->offset, data->value);
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_epu_clean - Clear EPU registers
|
||||
*/
|
||||
void fsl_epu_clean(void *epu_base)
|
||||
{
|
||||
u32 offset;
|
||||
|
||||
/* follow the exact sequence to clear the registers */
|
||||
/* Clear EPACRn */
|
||||
for (offset = EPACR0; offset <= EPACR15; offset += EPACR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPEVTCRn */
|
||||
for (offset = EPEVTCR0; offset <= EPEVTCR9; offset += EPEVTCR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPGCR */
|
||||
out_be32(epu_base + EPGCR, 0);
|
||||
|
||||
/* Clear EPSMCRn */
|
||||
for (offset = EPSMCR0; offset <= EPSMCR15; offset += EPSMCR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPCCRn */
|
||||
for (offset = EPCCR0; offset <= EPCCR31; offset += EPCCR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPCMPRn */
|
||||
for (offset = EPCMPR0; offset <= EPCMPR31; offset += EPCMPR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPCTRn */
|
||||
for (offset = EPCTR0; offset <= EPCTR31; offset += EPCTR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPIMCRn */
|
||||
for (offset = EPIMCR0; offset <= EPIMCR31; offset += EPIMCR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
|
||||
/* Clear EPXTRIGCRn */
|
||||
out_be32(epu_base + EPXTRIGCR, 0);
|
||||
|
||||
/* Clear EPECRn */
|
||||
for (offset = EPECR0; offset <= EPECR15; offset += EPECR_STRIDE)
|
||||
out_be32(epu_base + offset, 0);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __FSL_EPU_H
|
||||
#define __FSL_EPU_H
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
#define FSL_STRIDE_4B 4
|
||||
#define FSL_STRIDE_8B 8
|
||||
|
||||
/* Block offsets */
|
||||
#define EPU_BLOCK_OFFSET 0x00000000
|
||||
|
||||
/* EPGCR (Event Processor Global Control Register) */
|
||||
#define EPGCR 0x000
|
||||
|
||||
/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */
|
||||
#define EPEVTCR0 0x050
|
||||
#define EPEVTCR9 0x074
|
||||
#define EPEVTCR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
|
||||
#define EPXTRIGCR 0x090
|
||||
|
||||
/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */
|
||||
#define EPIMCR0 0x100
|
||||
#define EPIMCR31 0x17C
|
||||
#define EPIMCR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */
|
||||
#define EPSMCR0 0x200
|
||||
#define EPSMCR15 0x278
|
||||
#define EPSMCR_STRIDE FSL_STRIDE_8B
|
||||
|
||||
/* EPECR0-15 (Event Processor Event Control Registers) */
|
||||
#define EPECR0 0x300
|
||||
#define EPECR15 0x33C
|
||||
#define EPECR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
/* EPACR0-15 (Event Processor Action Control Registers) */
|
||||
#define EPACR0 0x400
|
||||
#define EPACR15 0x43C
|
||||
#define EPACR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
/* EPCCRi0-15 (Event Processor Counter Control Registers) */
|
||||
#define EPCCR0 0x800
|
||||
#define EPCCR15 0x83C
|
||||
#define EPCCR31 0x87C
|
||||
#define EPCCR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
/* EPCMPR0-15 (Event Processor Counter Compare Registers) */
|
||||
#define EPCMPR0 0x900
|
||||
#define EPCMPR15 0x93C
|
||||
#define EPCMPR31 0x97C
|
||||
#define EPCMPR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
/* EPCTR0-31 (Event Processor Counter Register) */
|
||||
#define EPCTR0 0xA00
|
||||
#define EPCTR31 0xA7C
|
||||
#define EPCTR_STRIDE FSL_STRIDE_4B
|
||||
|
||||
#define FSM_END_FLAG 0xFFFFFFFFUL
|
||||
|
||||
struct fsm_reg_vals {
|
||||
u32 offset;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
void fsl_epu_setup(void *epu_base);
|
||||
void fsl_epu_clean(void *epu_base);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/fsl_serdes.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include "fsl_ls1_serdes.h"
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_1
|
||||
static u64 serdes1_prtcl_map;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_2
|
||||
static u64 serdes2_prtcl_map;
|
||||
#endif
|
||||
|
||||
int is_serdes_configured(enum srds_prtcl device)
|
||||
{
|
||||
u64 ret = 0;
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_1
|
||||
if (!(serdes1_prtcl_map & (1ULL << NONE)))
|
||||
fsl_serdes_init();
|
||||
|
||||
ret |= (1ULL << device) & serdes1_prtcl_map;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_2
|
||||
if (!(serdes2_prtcl_map & (1ULL << NONE)))
|
||||
fsl_serdes_init();
|
||||
|
||||
ret |= (1ULL << device) & serdes2_prtcl_map;
|
||||
#endif
|
||||
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
int serdes_get_first_lane(u32 sd, enum srds_prtcl device)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
u32 cfg = in_be32(&gur->rcwsr[4]);
|
||||
int i;
|
||||
|
||||
switch (sd) {
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_1
|
||||
case FSL_SRDS_1:
|
||||
cfg &= RCWSR4_SRDS1_PRTCL_MASK;
|
||||
cfg >>= RCWSR4_SRDS1_PRTCL_SHIFT;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_2
|
||||
case FSL_SRDS_2:
|
||||
cfg &= RCWSR4_SRDS2_PRTCL_MASK;
|
||||
cfg >>= RCWSR4_SRDS2_PRTCL_SHIFT;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printf("invalid SerDes%d\n", sd);
|
||||
break;
|
||||
}
|
||||
/* Is serdes enabled at all? */
|
||||
if (unlikely(cfg == 0))
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < SRDS_MAX_LANES; i++) {
|
||||
if (serdes_get_prtcl(sd, cfg, i) == device)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
u64 serdes_init(u32 sd, u32 sd_addr, u32 sd_prctl_mask, u32 sd_prctl_shift)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
u64 serdes_prtcl_map = 0;
|
||||
u32 cfg;
|
||||
int lane;
|
||||
|
||||
cfg = in_be32(&gur->rcwsr[4]) & sd_prctl_mask;
|
||||
cfg >>= sd_prctl_shift;
|
||||
printf("Using SERDES%d Protocol: %d (0x%x)\n", sd + 1, cfg, cfg);
|
||||
|
||||
if (!is_serdes_prtcl_valid(sd, cfg))
|
||||
printf("SERDES%d[PRTCL] = 0x%x is not valid\n", sd + 1, cfg);
|
||||
|
||||
for (lane = 0; lane < SRDS_MAX_LANES; lane++) {
|
||||
enum srds_prtcl lane_prtcl = serdes_get_prtcl(sd, cfg, lane);
|
||||
|
||||
serdes_prtcl_map |= (1ULL << lane_prtcl);
|
||||
}
|
||||
|
||||
/* Set the first bit to indicate serdes has been initialized */
|
||||
serdes_prtcl_map |= (1ULL << NONE);
|
||||
|
||||
return serdes_prtcl_map;
|
||||
}
|
||||
|
||||
void fsl_serdes_init(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_1
|
||||
if (!(serdes1_prtcl_map & (1ULL << NONE)))
|
||||
serdes1_prtcl_map = serdes_init(FSL_SRDS_1,
|
||||
CONFIG_SYS_FSL_SERDES_ADDR,
|
||||
RCWSR4_SRDS1_PRTCL_MASK,
|
||||
RCWSR4_SRDS1_PRTCL_SHIFT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_SRDS_2
|
||||
if (!(serdes2_prtcl_map & (1ULL << NONE)))
|
||||
serdes2_prtcl_map = serdes_init(FSL_SRDS_2,
|
||||
CONFIG_SYS_FSL_SERDES_ADDR +
|
||||
FSL_SRDS_2 * 0x1000,
|
||||
RCWSR4_SRDS2_PRTCL_MASK,
|
||||
RCWSR4_SRDS2_PRTCL_SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *serdes_clock_to_string(u32 clock)
|
||||
{
|
||||
switch (clock) {
|
||||
case SRDS_PLLCR0_RFCK_SEL_100:
|
||||
return "100";
|
||||
case SRDS_PLLCR0_RFCK_SEL_125:
|
||||
return "125";
|
||||
default:
|
||||
return "100";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __FSL_LS1_SERDES_H
|
||||
#define __FSL_LS1_SERDES_H
|
||||
|
||||
int is_serdes_prtcl_valid(int serdes, u32 prtcl);
|
||||
int serdes_lane_enabled(int lane);
|
||||
#endif /* __FSL_LS1_SERDES_H */
|
||||
@@ -0,0 +1,246 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2016 Freescale Semiconductor, Inc.
|
||||
* Author: Hongbo Zhang <hongbo.zhang@nxp.com>
|
||||
* This file implements LS102X platform PSCI SYSTEM-SUSPEND function
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/psci.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <fsl_immap.h>
|
||||
#include "fsl_epu.h"
|
||||
|
||||
#define __secure __attribute__((section("._secure.text")))
|
||||
|
||||
#define CCSR_GICD_CTLR 0x1000
|
||||
#define CCSR_GICC_CTLR 0x2000
|
||||
#define DCSR_RCPM_CG1CR0 0x31c
|
||||
#define DCSR_RCPM_CSTTACR0 0xb00
|
||||
#define DCFG_CRSTSR_WDRFR 0x8
|
||||
#define DDR_RESV_LEN 128
|
||||
|
||||
#ifdef CONFIG_LS1_DEEP_SLEEP
|
||||
/*
|
||||
* DDR controller initialization training breaks the first 128 bytes of DDR,
|
||||
* save them so that the bootloader can restore them while resuming.
|
||||
*/
|
||||
static void __secure ls1_save_ddr_head(void)
|
||||
{
|
||||
const char *src = (const char *)CONFIG_SYS_SDRAM_BASE;
|
||||
char *dest = (char *)(OCRAM_BASE_S_ADDR + OCRAM_S_SIZE - DDR_RESV_LEN);
|
||||
struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
int i;
|
||||
|
||||
out_le32(&scfg->sparecr[2], dest);
|
||||
|
||||
for (i = 0; i < DDR_RESV_LEN; i++)
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
static void __secure ls1_fsm_setup(void)
|
||||
{
|
||||
void *dcsr_epu_base = (void *)(CONFIG_SYS_DCSRBAR + EPU_BLOCK_OFFSET);
|
||||
void *dcsr_rcpm_base = (void *)SYS_FSL_DCSR_RCPM_ADDR;
|
||||
|
||||
out_be32(dcsr_rcpm_base + DCSR_RCPM_CSTTACR0, 0x00001001);
|
||||
out_be32(dcsr_rcpm_base + DCSR_RCPM_CG1CR0, 0x00000001);
|
||||
|
||||
fsl_epu_setup((void *)dcsr_epu_base);
|
||||
|
||||
/* Pull MCKE signal low before enabling deep sleep signal in FPGA */
|
||||
out_be32(dcsr_epu_base + EPECR0, 0x5);
|
||||
out_be32(dcsr_epu_base + EPSMCR15, 0x76300000);
|
||||
}
|
||||
|
||||
static void __secure ls1_deepsleep_irq_cfg(void)
|
||||
{
|
||||
struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
struct ccsr_rcpm __iomem *rcpm = (void *)CONFIG_SYS_FSL_RCPM_ADDR;
|
||||
u32 ippdexpcr0, ippdexpcr1, pmcintecr = 0;
|
||||
|
||||
/* Mask interrupts from GIC */
|
||||
out_be32(&rcpm->nfiqoutr, 0x0ffffffff);
|
||||
out_be32(&rcpm->nirqoutr, 0x0ffffffff);
|
||||
/* Mask deep sleep wake-up interrupts while entering deep sleep */
|
||||
out_be32(&rcpm->dsimskr, 0x0ffffffff);
|
||||
|
||||
ippdexpcr0 = in_be32(&rcpm->ippdexpcr0);
|
||||
/*
|
||||
* Workaround of errata A-008646
|
||||
* Errata states that read to register ippdexpcr1 always returns
|
||||
* zero irrespective of what value is written into it. So its value
|
||||
* is first saved to a spare register and then read from it
|
||||
*/
|
||||
ippdexpcr1 = in_be32(&scfg->sparecr[7]);
|
||||
|
||||
/*
|
||||
* To allow OCRAM to be used as wakeup source in deep sleep,
|
||||
* do not power it down.
|
||||
*/
|
||||
out_be32(&rcpm->ippdexpcr1, ippdexpcr1 | RCPM_IPPDEXPCR1_OCRAM1);
|
||||
|
||||
if (ippdexpcr0 & RCPM_IPPDEXPCR0_ETSEC)
|
||||
pmcintecr |= SCFG_PMCINTECR_ETSECRXG0 |
|
||||
SCFG_PMCINTECR_ETSECRXG1 |
|
||||
SCFG_PMCINTECR_ETSECERRG0 |
|
||||
SCFG_PMCINTECR_ETSECERRG1;
|
||||
|
||||
if (ippdexpcr0 & RCPM_IPPDEXPCR0_GPIO)
|
||||
pmcintecr |= SCFG_PMCINTECR_GPIO;
|
||||
|
||||
if (ippdexpcr1 & RCPM_IPPDEXPCR1_LPUART)
|
||||
pmcintecr |= SCFG_PMCINTECR_LPUART;
|
||||
|
||||
if (ippdexpcr1 & RCPM_IPPDEXPCR1_FLEXTIMER)
|
||||
pmcintecr |= SCFG_PMCINTECR_FTM;
|
||||
|
||||
/* Always set external IRQ pins as wakeup source */
|
||||
pmcintecr |= SCFG_PMCINTECR_IRQ0 | SCFG_PMCINTECR_IRQ1;
|
||||
|
||||
out_be32(&scfg->pmcintlecr, 0);
|
||||
/* Clear PMC interrupt status */
|
||||
out_be32(&scfg->pmcintsr, 0xffffffff);
|
||||
/* Enable wakeup interrupt during deep sleep */
|
||||
out_be32(&scfg->pmcintecr, pmcintecr);
|
||||
}
|
||||
|
||||
static void __secure ls1_delay(unsigned int loop)
|
||||
{
|
||||
while (loop--) {
|
||||
int i = 1000;
|
||||
while (i--)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static void __secure ls1_start_fsm(void)
|
||||
{
|
||||
void *dcsr_epu_base = (void *)(CONFIG_SYS_DCSRBAR + EPU_BLOCK_OFFSET);
|
||||
void *ccsr_gic_base = (void *)SYS_FSL_GIC_ADDR;
|
||||
struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
struct ccsr_ddr __iomem *ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR;
|
||||
|
||||
/* Set HRSTCR */
|
||||
setbits_be32(&scfg->hrstcr, 0x80000000);
|
||||
|
||||
/* Place DDR controller in self refresh mode */
|
||||
setbits_be32(&ddr->sdram_cfg_2, 0x80000000);
|
||||
|
||||
ls1_delay(2000);
|
||||
|
||||
/* Set EVT4_B to lock the signal MCKE down */
|
||||
out_be32(dcsr_epu_base + EPECR0, 0x0);
|
||||
|
||||
ls1_delay(2000);
|
||||
|
||||
out_be32(ccsr_gic_base + CCSR_GICD_CTLR, 0x0);
|
||||
out_be32(ccsr_gic_base + CCSR_GICC_CTLR, 0x0);
|
||||
|
||||
/* Enable all EPU Counters */
|
||||
setbits_be32(dcsr_epu_base + EPGCR, 0x80000000);
|
||||
|
||||
/* Enable SCU15 */
|
||||
setbits_be32(dcsr_epu_base + EPECR15, 0x90000004);
|
||||
|
||||
/* Enter WFI mode, and EPU FSM will start */
|
||||
__asm__ __volatile__ ("wfi" : : : "memory");
|
||||
|
||||
/* NEVER ENTER HERE */
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
static void __secure ls1_deep_sleep(u32 entry_point)
|
||||
{
|
||||
struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR;
|
||||
struct ccsr_rcpm __iomem *rcpm = (void *)CONFIG_SYS_FSL_RCPM_ADDR;
|
||||
#ifdef QIXIS_BASE
|
||||
u32 tmp;
|
||||
void *qixis_base = (void *)QIXIS_BASE;
|
||||
#endif
|
||||
|
||||
/* Enable cluster to enter the PCL10 state */
|
||||
out_be32(&scfg->clusterpmcr, SCFG_CLUSTERPMCR_WFIL2EN);
|
||||
|
||||
/* Save the first 128 bytes of DDR data */
|
||||
ls1_save_ddr_head();
|
||||
|
||||
/* Save the kernel resume entry */
|
||||
out_le32(&scfg->sparecr[3], entry_point);
|
||||
|
||||
/* Request to put cluster 0 in PCL10 state */
|
||||
setbits_be32(&rcpm->clpcl10setr, RCPM_CLPCL10SETR_C0);
|
||||
|
||||
/* Setup the registers of the EPU FSM for deep sleep */
|
||||
ls1_fsm_setup();
|
||||
|
||||
#ifdef QIXIS_BASE
|
||||
/* Connect the EVENT button to IRQ in FPGA */
|
||||
tmp = in_8(qixis_base + QIXIS_CTL_SYS);
|
||||
tmp &= ~QIXIS_CTL_SYS_EVTSW_MASK;
|
||||
tmp |= QIXIS_CTL_SYS_EVTSW_IRQ;
|
||||
out_8(qixis_base + QIXIS_CTL_SYS, tmp);
|
||||
|
||||
/* Enable deep sleep signals in FPGA */
|
||||
tmp = in_8(qixis_base + QIXIS_PWR_CTL2);
|
||||
tmp |= QIXIS_PWR_CTL2_PCTL;
|
||||
out_8(qixis_base + QIXIS_PWR_CTL2, tmp);
|
||||
|
||||
/* Pull down PCIe RST# */
|
||||
tmp = in_8(qixis_base + QIXIS_RST_FORCE_3);
|
||||
tmp |= QIXIS_RST_FORCE_3_PCIESLOT1;
|
||||
out_8(qixis_base + QIXIS_RST_FORCE_3, tmp);
|
||||
#endif
|
||||
|
||||
/* Enable Warm Device Reset */
|
||||
setbits_be32(&scfg->dpslpcr, SCFG_DPSLPCR_WDRR_EN);
|
||||
setbits_be32(&gur->crstsr, DCFG_CRSTSR_WDRFR);
|
||||
|
||||
/* Disable QE */
|
||||
setbits_be32(&gur->devdisr, CCSR_DEVDISR1_QE);
|
||||
|
||||
ls1_deepsleep_irq_cfg();
|
||||
|
||||
psci_v7_flush_dcache_all();
|
||||
|
||||
ls1_start_fsm();
|
||||
}
|
||||
|
||||
#else
|
||||
static void __secure ls1_sleep(void)
|
||||
{
|
||||
struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
struct ccsr_rcpm __iomem *rcpm = (void *)CONFIG_SYS_FSL_RCPM_ADDR;
|
||||
|
||||
#ifdef QIXIS_BASE
|
||||
u32 tmp;
|
||||
void *qixis_base = (void *)QIXIS_BASE;
|
||||
|
||||
/* Connect the EVENT button to IRQ in FPGA */
|
||||
tmp = in_8(qixis_base + QIXIS_CTL_SYS);
|
||||
tmp &= ~QIXIS_CTL_SYS_EVTSW_MASK;
|
||||
tmp |= QIXIS_CTL_SYS_EVTSW_IRQ;
|
||||
out_8(qixis_base + QIXIS_CTL_SYS, tmp);
|
||||
#endif
|
||||
|
||||
/* Enable cluster to enter the PCL10 state */
|
||||
out_be32(&scfg->clusterpmcr, SCFG_CLUSTERPMCR_WFIL2EN);
|
||||
|
||||
setbits_be32(&rcpm->powmgtcsr, RCPM_POWMGTCSR_LPM20_REQ);
|
||||
|
||||
__asm__ __volatile__ ("wfi" : : : "memory");
|
||||
}
|
||||
#endif
|
||||
|
||||
void __secure ls1_system_suspend(u32 fn, u32 entry_point, u32 context_id)
|
||||
{
|
||||
#ifdef CONFIG_LS1_DEEP_SLEEP
|
||||
ls1_deep_sleep(entry_point);
|
||||
#else
|
||||
ls1_sleep();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/fsl_serdes.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
|
||||
static u8 serdes_cfg_tbl[][SRDS_MAX_LANES] = {
|
||||
[0x00] = {PCIE1, PCIE1, PCIE1, PCIE1},
|
||||
[0x10] = {PCIE1, SATA1, PCIE2, PCIE2},
|
||||
[0x20] = {PCIE1, SGMII_TSEC1, PCIE2, SGMII_TSEC2},
|
||||
[0x30] = {PCIE1, SATA1, SGMII_TSEC1, SGMII_TSEC2},
|
||||
[0x40] = {PCIE1, PCIE1, SATA1, SGMII_TSEC2},
|
||||
[0x50] = {PCIE1, PCIE1, PCIE2, SGMII_TSEC2},
|
||||
[0x60] = {PCIE1, PCIE1, SGMII_TSEC1, SGMII_TSEC2},
|
||||
[0x70] = {PCIE1, SATA1, PCIE2, SGMII_TSEC2},
|
||||
[0x80] = {PCIE2, PCIE2, PCIE2, PCIE2},
|
||||
};
|
||||
|
||||
enum srds_prtcl serdes_get_prtcl(int serdes, int cfg, int lane)
|
||||
{
|
||||
return serdes_cfg_tbl[cfg][lane];
|
||||
}
|
||||
|
||||
int is_serdes_prtcl_valid(int serdes, u32 prtcl)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (prtcl >= ARRAY_SIZE(serdes_cfg_tbl))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < SRDS_MAX_LANES; i++) {
|
||||
if (serdes_cfg_tbl[prtcl][i] != NONE)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
* Author: Wang Dongsheng <dongsheng.wang@freescale.com>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/arch-armv7/generictimer.h>
|
||||
#include <asm/psci.h>
|
||||
|
||||
#define RCPM_TWAITSR 0x04C
|
||||
|
||||
#define SCFG_CORE0_SFT_RST 0x130
|
||||
#define SCFG_CORESRENCR 0x204
|
||||
|
||||
#define DCFG_CCSR_RSTCR 0x0B0
|
||||
#define DCFG_CCSR_RSTCR_RESET_REQ 0x2
|
||||
#define DCFG_CCSR_BRR 0x0E4
|
||||
#define DCFG_CCSR_SCRATCHRW1 0x200
|
||||
|
||||
#define PSCI_FN_PSCI_VERSION_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_CPU_SUSPEND_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_CPU_OFF_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_CPU_ON_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_AFFINITY_INFO_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_SYSTEM_OFF_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_SYSTEM_RESET_FEATURE_MASK 0x0
|
||||
#define PSCI_FN_SYSTEM_SUSPEND_FEATURE_MASK 0x0
|
||||
|
||||
.pushsection ._secure.text, "ax"
|
||||
|
||||
.arch_extension sec
|
||||
|
||||
.align 5
|
||||
|
||||
#define ONE_MS (COUNTER_FREQUENCY / 1000)
|
||||
#define RESET_WAIT (30 * ONE_MS)
|
||||
|
||||
.globl psci_version
|
||||
psci_version:
|
||||
movw r0, #0
|
||||
movt r0, #1
|
||||
|
||||
bx lr
|
||||
|
||||
_ls102x_psci_supported_table:
|
||||
.word ARM_PSCI_0_2_FN_PSCI_VERSION
|
||||
.word PSCI_FN_PSCI_VERSION_FEATURE_MASK
|
||||
.word ARM_PSCI_0_2_FN_CPU_SUSPEND
|
||||
.word PSCI_FN_CPU_SUSPEND_FEATURE_MASK
|
||||
.word ARM_PSCI_0_2_FN_CPU_OFF
|
||||
.word PSCI_FN_CPU_OFF_FEATURE_MASK
|
||||
.word ARM_PSCI_0_2_FN_CPU_ON
|
||||
.word PSCI_FN_CPU_ON_FEATURE_MASK
|
||||
.word ARM_PSCI_0_2_FN_AFFINITY_INFO
|
||||
.word PSCI_FN_AFFINITY_INFO_FEATURE_MASK
|
||||
.word ARM_PSCI_0_2_FN_SYSTEM_OFF
|
||||
.word PSCI_FN_SYSTEM_OFF_FEATURE_MASK
|
||||
.word ARM_PSCI_0_2_FN_SYSTEM_RESET
|
||||
.word PSCI_FN_SYSTEM_RESET_FEATURE_MASK
|
||||
.word ARM_PSCI_1_0_FN_SYSTEM_SUSPEND
|
||||
.word PSCI_FN_SYSTEM_SUSPEND_FEATURE_MASK
|
||||
.word 0
|
||||
.word ARM_PSCI_RET_NI
|
||||
|
||||
.globl psci_features
|
||||
psci_features:
|
||||
adr r2, _ls102x_psci_supported_table
|
||||
1: ldr r3, [r2]
|
||||
cmp r3, #0
|
||||
beq out_psci_features
|
||||
cmp r1, r3
|
||||
addne r2, r2, #8
|
||||
bne 1b
|
||||
|
||||
out_psci_features:
|
||||
ldr r0, [r2, #4]
|
||||
bx lr
|
||||
|
||||
@ r0: return value ARM_PSCI_RET_SUCCESS or ARM_PSCI_RET_INVAL
|
||||
@ r1: input target CPU ID in MPIDR format, original value in r1 may be dropped
|
||||
@ r4: output validated CPU ID if ARM_PSCI_RET_SUCCESS returns, meaningless for
|
||||
@ ARM_PSCI_RET_INVAL,suppose caller saves r4 before calling
|
||||
LENTRY(psci_check_target_cpu_id)
|
||||
@ Get the real CPU number
|
||||
and r4, r1, #0xff
|
||||
mov r0, #ARM_PSCI_RET_INVAL
|
||||
|
||||
@ Bit[31:24], bits must be zero.
|
||||
tst r1, #0xff000000
|
||||
bxne lr
|
||||
|
||||
@ Affinity level 2 - Cluster: only one cluster in LS1021xa.
|
||||
tst r1, #0xff0000
|
||||
bxne lr
|
||||
|
||||
@ Affinity level 1 - Processors: should be in 0xf00 format.
|
||||
lsr r1, r1, #8
|
||||
teq r1, #0xf
|
||||
bxne lr
|
||||
|
||||
@ Affinity level 0 - CPU: only 0, 1 are valid in LS1021xa.
|
||||
cmp r4, #2
|
||||
bxge lr
|
||||
|
||||
mov r0, #ARM_PSCI_RET_SUCCESS
|
||||
bx lr
|
||||
ENDPROC(psci_check_target_cpu_id)
|
||||
|
||||
@ r1 = target CPU
|
||||
@ r2 = target PC
|
||||
.globl psci_cpu_on
|
||||
psci_cpu_on:
|
||||
push {r4, r5, r6, lr}
|
||||
|
||||
@ Clear and Get the correct CPU number
|
||||
@ r1 = 0xf01
|
||||
bl psci_check_target_cpu_id
|
||||
cmp r0, #ARM_PSCI_RET_INVAL
|
||||
beq out_psci_cpu_on
|
||||
|
||||
mov r0, r4
|
||||
mov r1, r2
|
||||
mov r2, r3
|
||||
bl psci_save
|
||||
mov r1, r4
|
||||
|
||||
@ Get DCFG base address
|
||||
movw r4, #(CONFIG_SYS_FSL_GUTS_ADDR & 0xffff)
|
||||
movt r4, #(CONFIG_SYS_FSL_GUTS_ADDR >> 16)
|
||||
|
||||
@ Detect target CPU state
|
||||
ldr r2, [r4, #DCFG_CCSR_BRR]
|
||||
rev r2, r2
|
||||
lsr r2, r2, r1
|
||||
ands r2, r2, #1
|
||||
beq holdoff_release
|
||||
|
||||
@ Reset target CPU
|
||||
@ Get SCFG base address
|
||||
movw r0, #(CONFIG_SYS_FSL_SCFG_ADDR & 0xffff)
|
||||
movt r0, #(CONFIG_SYS_FSL_SCFG_ADDR >> 16)
|
||||
|
||||
@ Enable CORE Soft Reset
|
||||
movw r5, #0
|
||||
movt r5, #(1 << 15)
|
||||
rev r5, r5
|
||||
str r5, [r0, #SCFG_CORESRENCR]
|
||||
|
||||
@ Get CPUx offset register
|
||||
mov r6, #0x4
|
||||
mul r6, r6, r1
|
||||
add r2, r0, r6
|
||||
|
||||
@ Do reset on target CPU
|
||||
movw r5, #0
|
||||
movt r5, #(1 << 15)
|
||||
rev r5, r5
|
||||
str r5, [r2, #SCFG_CORE0_SFT_RST]
|
||||
|
||||
@ Wait target CPU up
|
||||
timer_wait r2, RESET_WAIT
|
||||
|
||||
@ Disable CORE soft reset
|
||||
mov r5, #0
|
||||
str r5, [r0, #SCFG_CORESRENCR]
|
||||
|
||||
holdoff_release:
|
||||
@ Release on target CPU
|
||||
ldr r2, [r4, #DCFG_CCSR_BRR]
|
||||
mov r6, #1
|
||||
lsl r6, r6, r1 @ 32 bytes per CPU
|
||||
|
||||
rev r6, r6
|
||||
orr r2, r2, r6
|
||||
str r2, [r4, #DCFG_CCSR_BRR]
|
||||
|
||||
@ Set secondary boot entry
|
||||
ldr r6, =psci_cpu_entry
|
||||
rev r6, r6
|
||||
str r6, [r4, #DCFG_CCSR_SCRATCHRW1]
|
||||
|
||||
isb
|
||||
dsb
|
||||
|
||||
@ Return
|
||||
mov r0, #ARM_PSCI_RET_SUCCESS
|
||||
|
||||
out_psci_cpu_on:
|
||||
pop {r4, r5, r6, lr}
|
||||
bx lr
|
||||
|
||||
.globl psci_cpu_off
|
||||
psci_cpu_off:
|
||||
bl psci_cpu_off_common
|
||||
|
||||
1: wfi
|
||||
b 1b
|
||||
|
||||
.globl psci_affinity_info
|
||||
psci_affinity_info:
|
||||
push {lr}
|
||||
|
||||
mov r0, #ARM_PSCI_RET_INVAL
|
||||
|
||||
@ Verify Affinity level
|
||||
cmp r2, #0
|
||||
bne out_affinity_info
|
||||
|
||||
bl psci_check_target_cpu_id
|
||||
cmp r0, #ARM_PSCI_RET_INVAL
|
||||
beq out_affinity_info
|
||||
mov r1, r4
|
||||
|
||||
@ Get RCPM base address
|
||||
movw r4, #(CONFIG_SYS_FSL_RCPM_ADDR & 0xffff)
|
||||
movt r4, #(CONFIG_SYS_FSL_RCPM_ADDR >> 16)
|
||||
|
||||
mov r0, #PSCI_AFFINITY_LEVEL_ON
|
||||
|
||||
@ Detect target CPU state
|
||||
ldr r2, [r4, #RCPM_TWAITSR]
|
||||
rev r2, r2
|
||||
lsr r2, r2, r1
|
||||
ands r2, r2, #1
|
||||
beq out_affinity_info
|
||||
|
||||
mov r0, #PSCI_AFFINITY_LEVEL_OFF
|
||||
|
||||
out_affinity_info:
|
||||
pop {pc}
|
||||
|
||||
.globl psci_system_reset
|
||||
psci_system_reset:
|
||||
@ Get DCFG base address
|
||||
movw r1, #(CONFIG_SYS_FSL_GUTS_ADDR & 0xffff)
|
||||
movt r1, #(CONFIG_SYS_FSL_GUTS_ADDR >> 16)
|
||||
|
||||
mov r2, #DCFG_CCSR_RSTCR_RESET_REQ
|
||||
rev r2, r2
|
||||
str r2, [r1, #DCFG_CCSR_RSTCR]
|
||||
|
||||
1: wfi
|
||||
b 1b
|
||||
|
||||
.globl psci_system_suspend
|
||||
psci_system_suspend:
|
||||
push {lr}
|
||||
|
||||
bl ls1_system_suspend
|
||||
|
||||
pop {pc}
|
||||
|
||||
.popsection
|
||||
@@ -0,0 +1,241 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/fsl_serdes.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <asm/arch/ls102xa_soc.h>
|
||||
#include <asm/arch/ls102xa_stream_id.h>
|
||||
#include <fsl_csu.h>
|
||||
#include <fsl_ddr_sdram.h>
|
||||
|
||||
struct liodn_id_table sec_liodn_tbl[] = {
|
||||
SET_SEC_JR_LIODN_ENTRY(0, 0x10, 0x10),
|
||||
SET_SEC_JR_LIODN_ENTRY(1, 0x10, 0x10),
|
||||
SET_SEC_JR_LIODN_ENTRY(2, 0x10, 0x10),
|
||||
SET_SEC_JR_LIODN_ENTRY(3, 0x10, 0x10),
|
||||
SET_SEC_RTIC_LIODN_ENTRY(a, 0x10),
|
||||
SET_SEC_RTIC_LIODN_ENTRY(b, 0x10),
|
||||
SET_SEC_RTIC_LIODN_ENTRY(c, 0x10),
|
||||
SET_SEC_RTIC_LIODN_ENTRY(d, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(0, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(1, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(2, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(3, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(4, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(5, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(6, 0x10, 0x10),
|
||||
SET_SEC_DECO_LIODN_ENTRY(7, 0x10, 0x10),
|
||||
};
|
||||
|
||||
struct smmu_stream_id dev_stream_id[] = {
|
||||
{ 0x100, 0x01, "ETSEC MAC1" },
|
||||
{ 0x104, 0x02, "ETSEC MAC2" },
|
||||
{ 0x108, 0x03, "ETSEC MAC3" },
|
||||
{ 0x10c, 0x04, "PEX1" },
|
||||
{ 0x110, 0x05, "PEX2" },
|
||||
{ 0x114, 0x06, "qDMA" },
|
||||
{ 0x118, 0x07, "SATA" },
|
||||
{ 0x11c, 0x08, "USB3" },
|
||||
{ 0x120, 0x09, "QE" },
|
||||
{ 0x124, 0x0a, "eSDHC" },
|
||||
{ 0x128, 0x0b, "eMA" },
|
||||
{ 0x14c, 0x0c, "2D-ACE" },
|
||||
{ 0x150, 0x0d, "USB2" },
|
||||
{ 0x18c, 0x0e, "DEBUG" },
|
||||
};
|
||||
|
||||
unsigned int get_soc_major_rev(void)
|
||||
{
|
||||
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
|
||||
unsigned int svr, major;
|
||||
|
||||
svr = in_be32(&gur->svr);
|
||||
major = SVR_MAJ(svr);
|
||||
|
||||
return major;
|
||||
}
|
||||
|
||||
static void erratum_a009008(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A009008
|
||||
u32 __iomem *scfg = (u32 __iomem *)SCFG_BASE;
|
||||
|
||||
clrsetbits_be32(scfg + SCFG_USB3PRM1CR / 4,
|
||||
0xF << 6,
|
||||
SCFG_USB_TXVREFTUNE << 6);
|
||||
#endif /* CONFIG_SYS_FSL_ERRATUM_A009008 */
|
||||
}
|
||||
|
||||
static void erratum_a009798(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A009798
|
||||
u32 __iomem *scfg = (u32 __iomem *)SCFG_BASE;
|
||||
|
||||
clrbits_be32(scfg + SCFG_USB3PRM1CR / 4,
|
||||
SCFG_USB_SQRXTUNE_MASK << 23);
|
||||
#endif /* CONFIG_SYS_FSL_ERRATUM_A009798 */
|
||||
}
|
||||
|
||||
static void erratum_a008997(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A008997
|
||||
u32 __iomem *scfg = (u32 __iomem *)SCFG_BASE;
|
||||
|
||||
clrsetbits_be32(scfg + SCFG_USB3PRM2CR / 4,
|
||||
SCFG_USB_PCSTXSWINGFULL_MASK,
|
||||
SCFG_USB_PCSTXSWINGFULL_VAL);
|
||||
#endif /* CONFIG_SYS_FSL_ERRATUM_A008997 */
|
||||
}
|
||||
|
||||
static void erratum_a009007(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A009007
|
||||
void __iomem *usb_phy = (void __iomem *)USB_PHY_BASE;
|
||||
|
||||
out_le16(usb_phy + USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_1);
|
||||
out_le16(usb_phy + USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_2);
|
||||
out_le16(usb_phy + USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_3);
|
||||
out_le16(usb_phy + USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_4);
|
||||
#endif /* CONFIG_SYS_FSL_ERRATUM_A009007 */
|
||||
}
|
||||
|
||||
static void erratum_a008850_early(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A008850
|
||||
/* part 1 of 2 */
|
||||
struct ccsr_cci400 __iomem *cci = (void *)(CONFIG_SYS_IMMR +
|
||||
CONFIG_SYS_CCI400_OFFSET);
|
||||
struct ccsr_ddr __iomem *ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR;
|
||||
|
||||
/* disables propagation of barrier transactions to DDRC from CCI400 */
|
||||
out_le32(&cci->ctrl_ord, CCI400_CTRLORD_TERM_BARRIER);
|
||||
|
||||
/* disable the re-ordering in DDRC */
|
||||
out_be32(&ddr->eor, DDR_EOR_RD_REOD_DIS | DDR_EOR_WD_REOD_DIS);
|
||||
#endif
|
||||
}
|
||||
|
||||
void erratum_a008850_post(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A008850
|
||||
/* part 2 of 2 */
|
||||
struct ccsr_cci400 __iomem *cci = (void *)(CONFIG_SYS_IMMR +
|
||||
CONFIG_SYS_CCI400_OFFSET);
|
||||
struct ccsr_ddr __iomem *ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR;
|
||||
u32 tmp;
|
||||
|
||||
/* enable propagation of barrier transactions to DDRC from CCI400 */
|
||||
out_le32(&cci->ctrl_ord, CCI400_CTRLORD_EN_BARRIER);
|
||||
|
||||
/* enable the re-ordering in DDRC */
|
||||
tmp = in_be32(&ddr->eor);
|
||||
tmp &= ~(DDR_EOR_RD_REOD_DIS | DDR_EOR_WD_REOD_DIS);
|
||||
out_be32(&ddr->eor, tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void s_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A010315
|
||||
void erratum_a010315(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = PCIE1; i <= PCIE2; i++)
|
||||
if (!is_serdes_configured(i)) {
|
||||
debug("PCIe%d: disabled all R/W permission!\n", i);
|
||||
set_pcie_ns_access(i, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int arch_soc_init(void)
|
||||
{
|
||||
struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR;
|
||||
struct ccsr_cci400 *cci = (struct ccsr_cci400 *)(CONFIG_SYS_IMMR +
|
||||
CONFIG_SYS_CCI400_OFFSET);
|
||||
unsigned int major;
|
||||
|
||||
#ifdef CONFIG_LAYERSCAPE_NS_ACCESS
|
||||
enable_layerscape_ns_access();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSL_QSPI
|
||||
out_be32(&scfg->qspi_cfg, SCFG_QSPI_CLKSEL);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VIDEO_FSL_DCU_FB
|
||||
out_be32(&scfg->pixclkcr, SCFG_PIXCLKCR_PXCKEN);
|
||||
#endif
|
||||
|
||||
/* Configure Little endian for SAI, ASRC and SPDIF */
|
||||
out_be32(&scfg->endiancr, SCFG_ENDIANCR_LE);
|
||||
|
||||
/*
|
||||
* Enable snoop requests and DVM message requests for
|
||||
* All the slave insterfaces.
|
||||
*/
|
||||
out_le32(&cci->slave[0].snoop_ctrl,
|
||||
CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN);
|
||||
out_le32(&cci->slave[1].snoop_ctrl,
|
||||
CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN);
|
||||
out_le32(&cci->slave[2].snoop_ctrl,
|
||||
CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN);
|
||||
out_le32(&cci->slave[4].snoop_ctrl,
|
||||
CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN);
|
||||
|
||||
major = get_soc_major_rev();
|
||||
if (major == SOC_MAJOR_VER_1_0) {
|
||||
/*
|
||||
* Set CCI-400 Slave interface S1, S2 Shareable Override
|
||||
* Register All transactions are treated as non-shareable
|
||||
*/
|
||||
out_le32(&cci->slave[1].sha_ord, CCI400_SHAORD_NON_SHAREABLE);
|
||||
out_le32(&cci->slave[2].sha_ord, CCI400_SHAORD_NON_SHAREABLE);
|
||||
}
|
||||
|
||||
/* Enable all the snoop signal for various masters */
|
||||
out_be32(&scfg->snpcnfgcr, SCFG_SNPCNFGCR_SEC_RD_WR |
|
||||
SCFG_SNPCNFGCR_DCU_RD_WR |
|
||||
SCFG_SNPCNFGCR_SATA_RD_WR |
|
||||
SCFG_SNPCNFGCR_USB3_RD_WR |
|
||||
SCFG_SNPCNFGCR_DBG_RD_WR |
|
||||
SCFG_SNPCNFGCR_EDMA_SNP);
|
||||
|
||||
/*
|
||||
* Memory controller require a register write before being enabled.
|
||||
* Affects: DDR
|
||||
* Register: EDDRTQCFG
|
||||
* Description: Memory controller performance is not optimal with
|
||||
* default internal target queue register values.
|
||||
* Workaround: Write a value of 63b2_0042h to address: 157_020Ch.
|
||||
*/
|
||||
out_be32(&scfg->eddrtqcfg, 0x63b20042);
|
||||
|
||||
/* Erratum */
|
||||
erratum_a008850_early();
|
||||
erratum_a009008();
|
||||
erratum_a009798();
|
||||
erratum_a008997();
|
||||
erratum_a009007();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ls102xa_smmu_stream_id_init(void)
|
||||
{
|
||||
ls1021x_config_caam_stream_id(sec_liodn_tbl,
|
||||
ARRAY_SIZE(sec_liodn_tbl));
|
||||
|
||||
ls102xa_config_smmu_stream_id(dev_stream_id,
|
||||
ARRAY_SIZE(dev_stream_id));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <spl.h>
|
||||
|
||||
u32 spl_boot_device(void)
|
||||
{
|
||||
#ifdef CONFIG_SPL_MMC_SUPPORT
|
||||
return BOOT_DEVICE_MMC1;
|
||||
#endif
|
||||
return BOOT_DEVICE_NAND;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <div64.h>
|
||||
#include <asm/arch/immap_ls102xa.h>
|
||||
#include <asm/arch/clock.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* This function is intended for SHORT delays only.
|
||||
* It will overflow at around 10 seconds @ 400MHz,
|
||||
* or 20 seconds @ 200MHz.
|
||||
*/
|
||||
unsigned long usec2ticks(unsigned long usec)
|
||||
{
|
||||
ulong ticks;
|
||||
|
||||
if (usec < 1000)
|
||||
ticks = ((usec * (get_tbclk()/1000)) + 500) / 1000;
|
||||
else
|
||||
ticks = ((usec / 10) * (get_tbclk() / 100000));
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
static inline unsigned long long tick_to_time(unsigned long long tick)
|
||||
{
|
||||
unsigned long freq;
|
||||
|
||||
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
|
||||
|
||||
tick *= CONFIG_SYS_HZ;
|
||||
do_div(tick, freq);
|
||||
|
||||
return tick;
|
||||
}
|
||||
|
||||
static inline unsigned long long us_to_tick(unsigned long long usec)
|
||||
{
|
||||
unsigned long freq;
|
||||
|
||||
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
|
||||
|
||||
usec = usec * freq + 999999;
|
||||
do_div(usec, 1000000);
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
struct sctr_regs *sctr = (struct sctr_regs *)SCTR_BASE_ADDR;
|
||||
unsigned long ctrl, freq;
|
||||
unsigned long long val;
|
||||
|
||||
/* Enable System Counter */
|
||||
writel(SYS_COUNTER_CTRL_ENABLE, &sctr->cntcr);
|
||||
|
||||
freq = COUNTER_FREQUENCY;
|
||||
asm("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq));
|
||||
|
||||
/* Set PL1 Physical Timer Ctrl */
|
||||
ctrl = ARCH_TIMER_CTRL_ENABLE;
|
||||
asm("mcr p15, 0, %0, c14, c2, 1" : : "r" (ctrl));
|
||||
|
||||
/* Set PL1 Physical Comp Value */
|
||||
val = TIMER_COMP_VAL;
|
||||
asm("mcrr p15, 2, %Q0, %R0, c14" : : "r" (val));
|
||||
|
||||
gd->arch.tbl = 0;
|
||||
gd->arch.tbu = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
unsigned long long now;
|
||||
|
||||
asm("mrrc p15, 0, %Q0, %R0, c14" : "=r" (now));
|
||||
|
||||
gd->arch.tbl = (unsigned long)(now & 0xffffffff);
|
||||
gd->arch.tbu = (unsigned long)(now >> 32);
|
||||
|
||||
return now;
|
||||
}
|
||||
|
||||
unsigned long get_timer(ulong base)
|
||||
{
|
||||
return tick_to_time(get_ticks()) - base;
|
||||
}
|
||||
|
||||
/* delay x useconds and preserve advance timstamp value */
|
||||
void __udelay(unsigned long usec)
|
||||
{
|
||||
unsigned long long start;
|
||||
unsigned long tmo;
|
||||
|
||||
start = get_ticks(); /* get current timestamp */
|
||||
tmo = us_to_tick(usec); /* convert usecs to ticks */
|
||||
|
||||
while ((get_ticks() - start) < tmo)
|
||||
; /* loop till time has passed */
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (timebase clock frequency).
|
||||
* On ARM it returns the number of timer ticks per second.
|
||||
*/
|
||||
unsigned long get_tbclk(void)
|
||||
{
|
||||
unsigned long freq;
|
||||
|
||||
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
|
||||
|
||||
return freq;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Cortex-R Memory Protection Unit specific code
|
||||
*
|
||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Lokesh Vutla <lokeshvutla@ti.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/barriers.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include <asm/armv7_mpu.h>
|
||||
|
||||
/* MPU Type register definitions */
|
||||
#define MPUIR_S_SHIFT 0
|
||||
#define MPUIR_S_MASK BIT(MPUIR_S_SHIFT)
|
||||
#define MPUIR_DREGION_SHIFT 8
|
||||
#define MPUIR_DREGION_MASK (0xff << 8)
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* The Memory Protection Unit(MPU) allows to partition memory into regions
|
||||
* and set individual protection attributes for each region. In absence
|
||||
* of MPU a default map[1] will take effect. make sure to run this code
|
||||
* from a region which has execution permissions by default.
|
||||
* [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0460d/I1002400.html
|
||||
*/
|
||||
|
||||
void disable_mpu(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = get_cr();
|
||||
reg &= ~CR_M;
|
||||
dsb();
|
||||
set_cr(reg);
|
||||
isb();
|
||||
}
|
||||
|
||||
void enable_mpu(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = get_cr();
|
||||
reg |= CR_M;
|
||||
dsb();
|
||||
set_cr(reg);
|
||||
isb();
|
||||
}
|
||||
|
||||
int mpu_enabled(void)
|
||||
{
|
||||
return get_cr() & CR_M;
|
||||
}
|
||||
|
||||
void mpu_config(struct mpu_region_config *rgn)
|
||||
{
|
||||
u32 attr, val;
|
||||
|
||||
attr = get_attr_encoding(rgn->mr_attr);
|
||||
|
||||
/* MPU Region Number Register */
|
||||
asm volatile ("mcr p15, 0, %0, c6, c2, 0" : : "r" (rgn->region_no));
|
||||
|
||||
/* MPU Region Base Address Register */
|
||||
asm volatile ("mcr p15, 0, %0, c6, c1, 0" : : "r" (rgn->start_addr));
|
||||
|
||||
/* MPU Region Size and Enable Register */
|
||||
if (rgn->reg_size)
|
||||
val = (rgn->reg_size << REGION_SIZE_SHIFT) | ENABLE_REGION;
|
||||
else
|
||||
val = DISABLE_REGION;
|
||||
asm volatile ("mcr p15, 0, %0, c6, c1, 2" : : "r" (val));
|
||||
|
||||
/* MPU Region Access Control Register */
|
||||
val = rgn->xn << XN_SHIFT | rgn->ap << AP_SHIFT | attr;
|
||||
asm volatile ("mcr p15, 0, %0, c6, c1, 4" : : "r" (val));
|
||||
}
|
||||
|
||||
void setup_mpu_regions(struct mpu_region_config *rgns, u32 num_rgns)
|
||||
{
|
||||
u32 num, i;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c0, c0, 4" : "=r" (num));
|
||||
num = (num & MPUIR_DREGION_MASK) >> MPUIR_DREGION_SHIFT;
|
||||
/* Regions to be configured cannot be greater than available regions */
|
||||
if (num < num_rgns)
|
||||
num_rgns = num;
|
||||
/**
|
||||
* Assuming dcache might not be enabled at this point, disabling
|
||||
* and invalidating only icache.
|
||||
*/
|
||||
icache_disable();
|
||||
invalidate_icache_all();
|
||||
|
||||
disable_mpu();
|
||||
|
||||
for (i = 0; i < num_rgns; i++)
|
||||
mpu_config(&rgns[i]);
|
||||
|
||||
enable_mpu();
|
||||
|
||||
icache_enable();
|
||||
}
|
||||
|
||||
void enable_caches(void)
|
||||
{
|
||||
/*
|
||||
* setup_mpu_regions() might have enabled Icache. So add a check
|
||||
* before enabling Icache
|
||||
*/
|
||||
if (!icache_status())
|
||||
icache_enable();
|
||||
dcache_enable();
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* code for switching cores into non-secure state and into HYP mode
|
||||
*
|
||||
* Copyright (c) 2013 Andre Przywara <andre.przywara@linaro.org>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/gic.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/proc-armv/ptrace.h>
|
||||
|
||||
.arch_extension sec
|
||||
.arch_extension virt
|
||||
|
||||
.pushsection ._secure.text, "ax"
|
||||
|
||||
.align 5
|
||||
/* the vector table for secure state and HYP mode */
|
||||
_monitor_vectors:
|
||||
.word 0 /* reset */
|
||||
.word 0 /* undef */
|
||||
adr pc, _secure_monitor
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
|
||||
.macro is_cpu_virt_capable tmp
|
||||
mrc p15, 0, \tmp, c0, c1, 1 @ read ID_PFR1
|
||||
and \tmp, \tmp, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
|
||||
cmp \tmp, #(1 << CPUID_ARM_VIRT_SHIFT)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* secure monitor handler
|
||||
* U-Boot calls this "software interrupt" in start.S
|
||||
* This is executed on a "smc" instruction, we use a "smc #0" to switch
|
||||
* to non-secure state.
|
||||
* r0, r1, r2: passed to the callee
|
||||
* ip: target PC
|
||||
*/
|
||||
_secure_monitor:
|
||||
#ifdef CONFIG_ARMV7_PSCI
|
||||
ldr r5, =_psci_vectors @ Switch to the next monitor
|
||||
mcr p15, 0, r5, c12, c0, 1
|
||||
isb
|
||||
|
||||
@ Obtain a secure stack
|
||||
bl psci_stack_setup
|
||||
|
||||
@ Configure the PSCI backend
|
||||
push {r0, r1, r2, ip}
|
||||
bl psci_arch_init
|
||||
pop {r0, r1, r2, ip}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_773022
|
||||
mrc p15, 0, r5, c1, c0, 1
|
||||
orr r5, r5, #(1 << 1)
|
||||
mcr p15, 0, r5, c1, c0, 1
|
||||
isb
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_774769
|
||||
mrc p15, 0, r5, c1, c0, 1
|
||||
orr r5, r5, #(1 << 25)
|
||||
mcr p15, 0, r5, c1, c0, 1
|
||||
isb
|
||||
#endif
|
||||
|
||||
mrc p15, 0, r5, c1, c1, 0 @ read SCR
|
||||
bic r5, r5, #0x4a @ clear IRQ, EA, nET bits
|
||||
orr r5, r5, #0x31 @ enable NS, AW, FW bits
|
||||
@ FIQ preserved for secure mode
|
||||
mov r6, #SVC_MODE @ default mode is SVC
|
||||
is_cpu_virt_capable r4
|
||||
#ifdef CONFIG_ARMV7_VIRT
|
||||
orreq r5, r5, #0x100 @ allow HVC instruction
|
||||
moveq r6, #HYP_MODE @ Enter the kernel as HYP
|
||||
mrseq r3, sp_svc
|
||||
msreq sp_hyp, r3 @ migrate SP
|
||||
#endif
|
||||
|
||||
mcr p15, 0, r5, c1, c1, 0 @ write SCR (with NS bit set)
|
||||
isb
|
||||
|
||||
bne 1f
|
||||
|
||||
@ Reset CNTVOFF to 0 before leaving monitor mode
|
||||
mrc p15, 0, r4, c0, c1, 1 @ read ID_PFR1
|
||||
ands r4, r4, #CPUID_ARM_GENTIMER_MASK @ test arch timer bits
|
||||
movne r4, #0
|
||||
mcrrne p15, 4, r4, r4, c14 @ Reset CNTVOFF to zero
|
||||
1:
|
||||
mov lr, ip
|
||||
mov ip, #(F_BIT | I_BIT | A_BIT) @ Set A, I and F
|
||||
tst lr, #1 @ Check for Thumb PC
|
||||
orrne ip, ip, #T_BIT @ Set T if Thumb
|
||||
orr ip, ip, r6 @ Slot target mode in
|
||||
msr spsr_cxfs, ip @ Set full SPSR
|
||||
movs pc, lr @ ERET to non-secure
|
||||
|
||||
ENTRY(_do_nonsec_entry)
|
||||
mov ip, r0
|
||||
mov r0, r1
|
||||
mov r1, r2
|
||||
mov r2, r3
|
||||
smc #0
|
||||
ENDPROC(_do_nonsec_entry)
|
||||
|
||||
.macro get_cbar_addr addr
|
||||
#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
|
||||
ldr \addr, =CONFIG_ARM_GIC_BASE_ADDRESS
|
||||
#else
|
||||
mrc p15, 4, \addr, c15, c0, 0 @ read CBAR
|
||||
bfc \addr, #0, #15 @ clear reserved bits
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro get_gicd_addr addr
|
||||
get_cbar_addr \addr
|
||||
add \addr, \addr, #GIC_DIST_OFFSET @ GIC dist i/f offset
|
||||
.endm
|
||||
|
||||
.macro get_gicc_addr addr, tmp
|
||||
get_cbar_addr \addr
|
||||
is_cpu_virt_capable \tmp
|
||||
movne \tmp, #GIC_CPU_OFFSET_A9 @ GIC CPU offset for A9
|
||||
moveq \tmp, #GIC_CPU_OFFSET_A15 @ GIC CPU offset for A15/A7
|
||||
add \addr, \addr, \tmp
|
||||
.endm
|
||||
|
||||
#ifndef CONFIG_ARMV7_PSCI
|
||||
/*
|
||||
* Secondary CPUs start here and call the code for the core specific parts
|
||||
* of the non-secure and HYP mode transition. The GIC distributor specific
|
||||
* code has already been executed by a C function before.
|
||||
* Then they go back to wfi and wait to be woken up by the kernel again.
|
||||
*/
|
||||
ENTRY(_smp_pen)
|
||||
cpsid i
|
||||
cpsid f
|
||||
|
||||
bl _nonsec_init
|
||||
|
||||
adr r0, _smp_pen @ do not use this address again
|
||||
b smp_waitloop @ wait for IPIs, board specific
|
||||
ENDPROC(_smp_pen)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Switch a core to non-secure state.
|
||||
*
|
||||
* 1. initialize the GIC per-core interface
|
||||
* 2. allow coprocessor access in non-secure modes
|
||||
*
|
||||
* Called from smp_pen by secondary cores and directly by the BSP.
|
||||
* Do not assume that the stack is available and only use registers
|
||||
* r0-r3 and r12.
|
||||
*
|
||||
* PERIPHBASE is used to get the GIC address. This could be 40 bits long,
|
||||
* though, but we check this in C before calling this function.
|
||||
*/
|
||||
ENTRY(_nonsec_init)
|
||||
get_gicd_addr r3
|
||||
|
||||
mvn r1, #0 @ all bits to 1
|
||||
str r1, [r3, #GICD_IGROUPRn] @ allow private interrupts
|
||||
|
||||
get_gicc_addr r3, r1
|
||||
|
||||
mov r1, #3 @ Enable both groups
|
||||
str r1, [r3, #GICC_CTLR] @ and clear all other bits
|
||||
mov r1, #0xff
|
||||
str r1, [r3, #GICC_PMR] @ set priority mask register
|
||||
|
||||
mrc p15, 0, r0, c1, c1, 2
|
||||
movw r1, #0x3fff
|
||||
movt r1, #0x0004
|
||||
orr r0, r0, r1
|
||||
mcr p15, 0, r0, c1, c1, 2 @ NSACR = all copros to non-sec
|
||||
|
||||
/* The CNTFRQ register of the generic timer needs to be
|
||||
* programmed in secure state. Some primary bootloaders / firmware
|
||||
* omit this, so if the frequency is provided in the configuration,
|
||||
* we do this here instead.
|
||||
* But first check if we have the generic timer.
|
||||
*/
|
||||
#ifdef COUNTER_FREQUENCY
|
||||
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
|
||||
and r0, r0, #CPUID_ARM_GENTIMER_MASK @ mask arch timer bits
|
||||
cmp r0, #(1 << CPUID_ARM_GENTIMER_SHIFT)
|
||||
ldreq r1, =COUNTER_FREQUENCY
|
||||
mcreq p15, 0, r1, c14, c0, 0 @ write CNTFRQ
|
||||
#endif
|
||||
|
||||
adr r1, _monitor_vectors
|
||||
mcr p15, 0, r1, c12, c0, 1 @ set MVBAR to secure vectors
|
||||
isb
|
||||
|
||||
mov r0, r3 @ return GICC address
|
||||
bx lr
|
||||
ENDPROC(_nonsec_init)
|
||||
|
||||
#ifdef CONFIG_SMP_PEN_ADDR
|
||||
/* void __weak smp_waitloop(unsigned previous_address); */
|
||||
ENTRY(smp_waitloop)
|
||||
wfi
|
||||
ldr r1, =CONFIG_SMP_PEN_ADDR @ load start address
|
||||
ldr r1, [r1]
|
||||
#ifdef CONFIG_PEN_ADDR_BIG_ENDIAN
|
||||
rev r1, r1
|
||||
#endif
|
||||
cmp r0, r1 @ make sure we dont execute this code
|
||||
beq smp_waitloop @ again (due to a spurious wakeup)
|
||||
mov r0, r1
|
||||
b _do_nonsec_entry
|
||||
ENDPROC(smp_waitloop)
|
||||
.weak smp_waitloop
|
||||
#endif
|
||||
|
||||
.popsection
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Common PSCI functions
|
||||
*
|
||||
* Copyright (C) 2016 Chen-Yu Tsai
|
||||
* Author: Chen-Yu Tsai <wens@csie.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/macro.h>
|
||||
#include <asm/psci.h>
|
||||
#include <asm/secure.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
static u32 psci_target_pc[CONFIG_ARMV7_PSCI_NR_CPUS] __secure_data = { 0 };
|
||||
static u32 psci_context_id[CONFIG_ARMV7_PSCI_NR_CPUS] __secure_data = { 0 };
|
||||
|
||||
void __secure psci_save(int cpu, u32 pc, u32 context_id)
|
||||
{
|
||||
psci_target_pc[cpu] = pc;
|
||||
psci_context_id[cpu] = context_id;
|
||||
dsb();
|
||||
}
|
||||
|
||||
u32 __secure psci_get_target_pc(int cpu)
|
||||
{
|
||||
return psci_target_pc[cpu];
|
||||
}
|
||||
|
||||
u32 __secure psci_get_context_id(int cpu)
|
||||
{
|
||||
return psci_context_id[cpu];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (C) 2013,2014 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/macro.h>
|
||||
#include <asm/psci.h>
|
||||
|
||||
.pushsection ._secure.text, "ax"
|
||||
|
||||
.arch_extension sec
|
||||
|
||||
.align 5
|
||||
.globl _psci_vectors
|
||||
_psci_vectors:
|
||||
b default_psci_vector @ reset
|
||||
b default_psci_vector @ undef
|
||||
b _smc_psci @ smc
|
||||
b default_psci_vector @ pabort
|
||||
b default_psci_vector @ dabort
|
||||
b default_psci_vector @ hyp
|
||||
b default_psci_vector @ irq
|
||||
b psci_fiq_enter @ fiq
|
||||
|
||||
ENTRY(psci_fiq_enter)
|
||||
movs pc, lr
|
||||
ENDPROC(psci_fiq_enter)
|
||||
.weak psci_fiq_enter
|
||||
|
||||
ENTRY(default_psci_vector)
|
||||
movs pc, lr
|
||||
ENDPROC(default_psci_vector)
|
||||
.weak default_psci_vector
|
||||
|
||||
ENTRY(psci_version)
|
||||
ENTRY(psci_cpu_suspend)
|
||||
ENTRY(psci_cpu_off)
|
||||
ENTRY(psci_cpu_on)
|
||||
ENTRY(psci_affinity_info)
|
||||
ENTRY(psci_migrate)
|
||||
ENTRY(psci_migrate_info_type)
|
||||
ENTRY(psci_migrate_info_up_cpu)
|
||||
ENTRY(psci_system_off)
|
||||
ENTRY(psci_system_reset)
|
||||
ENTRY(psci_features)
|
||||
ENTRY(psci_cpu_freeze)
|
||||
ENTRY(psci_cpu_default_suspend)
|
||||
ENTRY(psci_node_hw_state)
|
||||
ENTRY(psci_system_suspend)
|
||||
ENTRY(psci_set_suspend_mode)
|
||||
ENTRY(psi_stat_residency)
|
||||
ENTRY(psci_stat_count)
|
||||
mov r0, #ARM_PSCI_RET_NI @ Return -1 (Not Implemented)
|
||||
mov pc, lr
|
||||
ENDPROC(psci_stat_count)
|
||||
ENDPROC(psi_stat_residency)
|
||||
ENDPROC(psci_set_suspend_mode)
|
||||
ENDPROC(psci_system_suspend)
|
||||
ENDPROC(psci_node_hw_state)
|
||||
ENDPROC(psci_cpu_default_suspend)
|
||||
ENDPROC(psci_cpu_freeze)
|
||||
ENDPROC(psci_features)
|
||||
ENDPROC(psci_system_reset)
|
||||
ENDPROC(psci_system_off)
|
||||
ENDPROC(psci_migrate_info_up_cpu)
|
||||
ENDPROC(psci_migrate_info_type)
|
||||
ENDPROC(psci_migrate)
|
||||
ENDPROC(psci_affinity_info)
|
||||
ENDPROC(psci_cpu_on)
|
||||
ENDPROC(psci_cpu_off)
|
||||
ENDPROC(psci_cpu_suspend)
|
||||
ENDPROC(psci_version)
|
||||
.weak psci_version
|
||||
.weak psci_cpu_suspend
|
||||
.weak psci_cpu_off
|
||||
.weak psci_cpu_on
|
||||
.weak psci_affinity_info
|
||||
.weak psci_migrate
|
||||
.weak psci_migrate_info_type
|
||||
.weak psci_migrate_info_up_cpu
|
||||
.weak psci_system_off
|
||||
.weak psci_system_reset
|
||||
.weak psci_features
|
||||
.weak psci_cpu_freeze
|
||||
.weak psci_cpu_default_suspend
|
||||
.weak psci_node_hw_state
|
||||
.weak psci_system_suspend
|
||||
.weak psci_set_suspend_mode
|
||||
.weak psi_stat_residency
|
||||
.weak psci_stat_count
|
||||
|
||||
_psci_table:
|
||||
.word ARM_PSCI_FN_CPU_SUSPEND
|
||||
.word psci_cpu_suspend
|
||||
.word ARM_PSCI_FN_CPU_OFF
|
||||
.word psci_cpu_off
|
||||
.word ARM_PSCI_FN_CPU_ON
|
||||
.word psci_cpu_on
|
||||
.word ARM_PSCI_FN_MIGRATE
|
||||
.word psci_migrate
|
||||
.word ARM_PSCI_0_2_FN_PSCI_VERSION
|
||||
.word psci_version
|
||||
.word ARM_PSCI_0_2_FN_CPU_SUSPEND
|
||||
.word psci_cpu_suspend
|
||||
.word ARM_PSCI_0_2_FN_CPU_OFF
|
||||
.word psci_cpu_off
|
||||
.word ARM_PSCI_0_2_FN_CPU_ON
|
||||
.word psci_cpu_on
|
||||
.word ARM_PSCI_0_2_FN_AFFINITY_INFO
|
||||
.word psci_affinity_info
|
||||
.word ARM_PSCI_0_2_FN_MIGRATE
|
||||
.word psci_migrate
|
||||
.word ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE
|
||||
.word psci_migrate_info_type
|
||||
.word ARM_PSCI_0_2_FN_MIGRATE_INFO_UP_CPU
|
||||
.word psci_migrate_info_up_cpu
|
||||
.word ARM_PSCI_0_2_FN_SYSTEM_OFF
|
||||
.word psci_system_off
|
||||
.word ARM_PSCI_0_2_FN_SYSTEM_RESET
|
||||
.word psci_system_reset
|
||||
.word ARM_PSCI_1_0_FN_PSCI_FEATURES
|
||||
.word psci_features
|
||||
.word ARM_PSCI_1_0_FN_CPU_FREEZE
|
||||
.word psci_cpu_freeze
|
||||
.word ARM_PSCI_1_0_FN_CPU_DEFAULT_SUSPEND
|
||||
.word psci_cpu_default_suspend
|
||||
.word ARM_PSCI_1_0_FN_NODE_HW_STATE
|
||||
.word psci_node_hw_state
|
||||
.word ARM_PSCI_1_0_FN_SYSTEM_SUSPEND
|
||||
.word psci_system_suspend
|
||||
.word ARM_PSCI_1_0_FN_SET_SUSPEND_MODE
|
||||
.word psci_set_suspend_mode
|
||||
.word ARM_PSCI_1_0_FN_STAT_RESIDENCY
|
||||
.word psi_stat_residency
|
||||
.word ARM_PSCI_1_0_FN_STAT_COUNT
|
||||
.word psci_stat_count
|
||||
.word 0
|
||||
.word 0
|
||||
|
||||
_smc_psci:
|
||||
push {r4-r7,lr}
|
||||
|
||||
@ Switch to secure
|
||||
mrc p15, 0, r7, c1, c1, 0
|
||||
bic r4, r7, #1
|
||||
mcr p15, 0, r4, c1, c1, 0
|
||||
isb
|
||||
|
||||
adr r4, _psci_table
|
||||
1: ldr r5, [r4] @ Load PSCI function ID
|
||||
ldr r6, [r4, #4] @ Load target PC
|
||||
cmp r5, #0 @ If reach the end, bail out
|
||||
moveq r0, #ARM_PSCI_RET_INVAL @ Return -2 (Invalid)
|
||||
beq 2f
|
||||
cmp r0, r5 @ If not matching, try next entry
|
||||
addne r4, r4, #8
|
||||
bne 1b
|
||||
|
||||
blx r6 @ Execute PSCI function
|
||||
|
||||
@ Switch back to non-secure
|
||||
2: mcr p15, 0, r7, c1, c1, 0
|
||||
|
||||
pop {r4-r7, lr}
|
||||
movs pc, lr @ Return to the kernel
|
||||
|
||||
@ Requires dense and single-cluster CPU ID space
|
||||
ENTRY(psci_get_cpu_id)
|
||||
mrc p15, 0, r0, c0, c0, 5 /* read MPIDR */
|
||||
and r0, r0, #0xff /* return CPU ID in cluster */
|
||||
bx lr
|
||||
ENDPROC(psci_get_cpu_id)
|
||||
.weak psci_get_cpu_id
|
||||
|
||||
/* Imported from Linux kernel */
|
||||
ENTRY(psci_v7_flush_dcache_all)
|
||||
stmfd sp!, {r4-r5, r7, r9-r11, lr}
|
||||
dmb @ ensure ordering with previous memory accesses
|
||||
mrc p15, 1, r0, c0, c0, 1 @ read clidr
|
||||
ands r3, r0, #0x7000000 @ extract loc from clidr
|
||||
mov r3, r3, lsr #23 @ left align loc bit field
|
||||
beq finished @ if loc is 0, then no need to clean
|
||||
mov r10, #0 @ start clean at cache level 0
|
||||
flush_levels:
|
||||
add r2, r10, r10, lsr #1 @ work out 3x current cache level
|
||||
mov r1, r0, lsr r2 @ extract cache type bits from clidr
|
||||
and r1, r1, #7 @ mask of the bits for current cache only
|
||||
cmp r1, #2 @ see what cache we have at this level
|
||||
blt skip @ skip if no cache, or just i-cache
|
||||
mrs r9, cpsr @ make cssr&csidr read atomic
|
||||
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
|
||||
isb @ isb to sych the new cssr&csidr
|
||||
mrc p15, 1, r1, c0, c0, 0 @ read the new csidr
|
||||
msr cpsr_c, r9
|
||||
and r2, r1, #7 @ extract the length of the cache lines
|
||||
add r2, r2, #4 @ add 4 (line length offset)
|
||||
ldr r4, =0x3ff
|
||||
ands r4, r4, r1, lsr #3 @ find maximum number on the way size
|
||||
clz r5, r4 @ find bit position of way size increment
|
||||
ldr r7, =0x7fff
|
||||
ands r7, r7, r1, lsr #13 @ extract max number of the index size
|
||||
loop1:
|
||||
mov r9, r7 @ create working copy of max index
|
||||
loop2:
|
||||
orr r11, r10, r4, lsl r5 @ factor way and cache number into r11
|
||||
orr r11, r11, r9, lsl r2 @ factor index number into r11
|
||||
mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way
|
||||
subs r9, r9, #1 @ decrement the index
|
||||
bge loop2
|
||||
subs r4, r4, #1 @ decrement the way
|
||||
bge loop1
|
||||
skip:
|
||||
add r10, r10, #2 @ increment cache number
|
||||
cmp r3, r10
|
||||
bgt flush_levels
|
||||
finished:
|
||||
mov r10, #0 @ swith back to cache level 0
|
||||
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
|
||||
dsb st
|
||||
isb
|
||||
ldmfd sp!, {r4-r5, r7, r9-r11, lr}
|
||||
bx lr
|
||||
ENDPROC(psci_v7_flush_dcache_all)
|
||||
|
||||
ENTRY(psci_disable_smp)
|
||||
mrc p15, 0, r0, c1, c0, 1 @ ACTLR
|
||||
bic r0, r0, #(1 << 6) @ Clear SMP bit
|
||||
mcr p15, 0, r0, c1, c0, 1 @ ACTLR
|
||||
isb
|
||||
dsb
|
||||
bx lr
|
||||
ENDPROC(psci_disable_smp)
|
||||
.weak psci_disable_smp
|
||||
|
||||
ENTRY(psci_enable_smp)
|
||||
mrc p15, 0, r0, c1, c0, 1 @ ACTLR
|
||||
orr r0, r0, #(1 << 6) @ Set SMP bit
|
||||
mcr p15, 0, r0, c1, c0, 1 @ ACTLR
|
||||
isb
|
||||
bx lr
|
||||
ENDPROC(psci_enable_smp)
|
||||
.weak psci_enable_smp
|
||||
|
||||
ENTRY(psci_cpu_off_common)
|
||||
push {lr}
|
||||
|
||||
bl psci_v7_flush_dcache_all
|
||||
|
||||
clrex @ Why???
|
||||
|
||||
mrc p15, 0, r0, c1, c0, 0 @ SCTLR
|
||||
bic r0, r0, #(1 << 2) @ Clear C bit
|
||||
mcr p15, 0, r0, c1, c0, 0 @ SCTLR
|
||||
isb
|
||||
dsb
|
||||
|
||||
bl psci_v7_flush_dcache_all
|
||||
|
||||
clrex @ Why???
|
||||
|
||||
bl psci_disable_smp
|
||||
|
||||
pop {lr}
|
||||
bx lr
|
||||
ENDPROC(psci_cpu_off_common)
|
||||
|
||||
@ The stacks are allocated in reverse order, i.e.
|
||||
@ the stack for CPU0 has the highest memory address.
|
||||
@
|
||||
@ -------------------- __secure_stack_end
|
||||
@ | CPU0 target PC |
|
||||
@ |------------------|
|
||||
@ | |
|
||||
@ | CPU0 stack |
|
||||
@ | |
|
||||
@ |------------------| __secure_stack_end - 1KB
|
||||
@ | . |
|
||||
@ | . |
|
||||
@ | . |
|
||||
@ | . |
|
||||
@ -------------------- __secure_stack_start
|
||||
@
|
||||
@ This expects CPU ID in r0 and returns stack top in r0
|
||||
LENTRY(psci_get_cpu_stack_top)
|
||||
@ stack top = __secure_stack_end - (cpuid << ARM_PSCI_STACK_SHIFT)
|
||||
ldr r3, =__secure_stack_end
|
||||
sub r0, r3, r0, LSL #ARM_PSCI_STACK_SHIFT
|
||||
sub r0, r0, #4 @ Save space for target PC
|
||||
bx lr
|
||||
ENDPROC(psci_get_cpu_stack_top)
|
||||
|
||||
@ {r0, r1, r2, ip} from _do_nonsec_entry(kernel_entry, 0, machid, r2) in
|
||||
@ arch/arm/lib/bootm.c:boot_jump_linux() must remain unchanged across
|
||||
@ this function.
|
||||
ENTRY(psci_stack_setup)
|
||||
mov r6, lr
|
||||
mov r7, r0
|
||||
bl psci_get_cpu_id @ CPU ID => r0
|
||||
bl psci_get_cpu_stack_top @ stack top => r0
|
||||
mov sp, r0
|
||||
mov r0, r7
|
||||
bx r6
|
||||
ENDPROC(psci_stack_setup)
|
||||
|
||||
ENTRY(psci_arch_init)
|
||||
mov pc, lr
|
||||
ENDPROC(psci_arch_init)
|
||||
.weak psci_arch_init
|
||||
|
||||
ENTRY(psci_arch_cpu_entry)
|
||||
mov pc, lr
|
||||
ENDPROC(psci_arch_cpu_entry)
|
||||
.weak psci_arch_cpu_entry
|
||||
|
||||
ENTRY(psci_cpu_entry)
|
||||
bl psci_enable_smp
|
||||
|
||||
bl _nonsec_init
|
||||
|
||||
bl psci_stack_setup
|
||||
|
||||
bl psci_arch_cpu_entry
|
||||
|
||||
bl psci_get_cpu_id @ CPU ID => r0
|
||||
mov r2, r0 @ CPU ID => r2
|
||||
bl psci_get_context_id @ context id => r0
|
||||
mov r1, r0 @ context id => r1
|
||||
mov r0, r2 @ CPU ID => r0
|
||||
bl psci_get_target_pc @ target PC => r0
|
||||
b _do_nonsec_entry
|
||||
ENDPROC(psci_cpu_entry)
|
||||
|
||||
.popsection
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2009 Samsung Electronics
|
||||
# Minkyu Kang <mk7.kang@samsung.com>
|
||||
|
||||
obj-y += cpu_info.o
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-y += timer.o
|
||||
obj-y += sromc.o
|
||||
obj-$(CONFIG_PWM) += pwm.o
|
||||
endif
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Minkyu Kang <mk7.kang@samsung.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <fdtdec.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Default is s5pc100 */
|
||||
unsigned int s5p_cpu_id = 0xC100;
|
||||
/* Default is EVT1 */
|
||||
unsigned int s5p_cpu_rev = 1;
|
||||
|
||||
#ifdef CONFIG_ARCH_CPU_INIT
|
||||
int arch_cpu_init(void)
|
||||
{
|
||||
s5p_set_cpu_id();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 get_device_type(void)
|
||||
{
|
||||
return s5p_cpu_id;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DISPLAY_CPUINFO
|
||||
int print_cpuinfo(void)
|
||||
{
|
||||
const char *cpu_model;
|
||||
int len;
|
||||
|
||||
/* For SoC with no real CPU ID in naming convention. */
|
||||
cpu_model = fdt_getprop(gd->fdt_blob, 0, "cpu-model", &len);
|
||||
if (cpu_model)
|
||||
printf("CPU: %.*s @ ", len, cpu_model);
|
||||
else
|
||||
printf("CPU: %s%X @ ", s5p_get_cpu_name(), s5p_cpu_id);
|
||||
|
||||
print_freq(get_arm_clk(), "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
*
|
||||
* Donghwa Lee <dh09.lee@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <pwm.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/pwm.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
int pwm_enable(int pwm_id)
|
||||
{
|
||||
const struct s5p_timer *pwm =
|
||||
(struct s5p_timer *)samsung_get_base_timer();
|
||||
unsigned long tcon;
|
||||
|
||||
tcon = readl(&pwm->tcon);
|
||||
tcon |= TCON_START(pwm_id);
|
||||
|
||||
writel(tcon, &pwm->tcon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pwm_disable(int pwm_id)
|
||||
{
|
||||
const struct s5p_timer *pwm =
|
||||
(struct s5p_timer *)samsung_get_base_timer();
|
||||
unsigned long tcon;
|
||||
|
||||
tcon = readl(&pwm->tcon);
|
||||
tcon &= ~TCON_START(pwm_id);
|
||||
|
||||
writel(tcon, &pwm->tcon);
|
||||
}
|
||||
|
||||
static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq)
|
||||
{
|
||||
unsigned long tin_parent_rate;
|
||||
unsigned int div;
|
||||
|
||||
tin_parent_rate = get_pwm_clk();
|
||||
|
||||
for (div = 2; div <= 16; div *= 2) {
|
||||
if ((tin_parent_rate / (div << 16)) < freq)
|
||||
return tin_parent_rate / div;
|
||||
}
|
||||
|
||||
return tin_parent_rate / 16;
|
||||
}
|
||||
|
||||
#define NS_IN_SEC 1000000000UL
|
||||
|
||||
int pwm_config(int pwm_id, int duty_ns, int period_ns)
|
||||
{
|
||||
const struct s5p_timer *pwm =
|
||||
(struct s5p_timer *)samsung_get_base_timer();
|
||||
unsigned int offset;
|
||||
unsigned long tin_rate;
|
||||
unsigned long tin_ns;
|
||||
unsigned long frequency;
|
||||
unsigned long tcon;
|
||||
unsigned long tcnt;
|
||||
unsigned long tcmp;
|
||||
|
||||
/*
|
||||
* We currently avoid using 64bit arithmetic by using the
|
||||
* fact that anything faster than 1GHz is easily representable
|
||||
* by 32bits.
|
||||
*/
|
||||
if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0)
|
||||
return -ERANGE;
|
||||
|
||||
if (duty_ns > period_ns)
|
||||
return -EINVAL;
|
||||
|
||||
frequency = NS_IN_SEC / period_ns;
|
||||
|
||||
/* Check to see if we are changing the clock rate of the PWM */
|
||||
tin_rate = pwm_calc_tin(pwm_id, frequency);
|
||||
|
||||
tin_ns = NS_IN_SEC / tin_rate;
|
||||
tcnt = period_ns / tin_ns;
|
||||
|
||||
/* Note, counters count down */
|
||||
tcmp = duty_ns / tin_ns;
|
||||
tcmp = tcnt - tcmp;
|
||||
|
||||
/* Update the PWM register block. */
|
||||
offset = pwm_id * 3;
|
||||
if (pwm_id < 4) {
|
||||
writel(tcnt, &pwm->tcntb0 + offset);
|
||||
writel(tcmp, &pwm->tcmpb0 + offset);
|
||||
}
|
||||
|
||||
tcon = readl(&pwm->tcon);
|
||||
tcon |= TCON_UPDATE(pwm_id);
|
||||
if (pwm_id < 4)
|
||||
tcon |= TCON_AUTO_RELOAD(pwm_id);
|
||||
else
|
||||
tcon |= TCON4_AUTO_RELOAD;
|
||||
writel(tcon, &pwm->tcon);
|
||||
|
||||
tcon &= ~TCON_UPDATE(pwm_id);
|
||||
writel(tcon, &pwm->tcon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pwm_init(int pwm_id, int div, int invert)
|
||||
{
|
||||
u32 val;
|
||||
const struct s5p_timer *pwm =
|
||||
(struct s5p_timer *)samsung_get_base_timer();
|
||||
unsigned long ticks_per_period;
|
||||
unsigned int offset, prescaler;
|
||||
|
||||
/*
|
||||
* Timer Freq(HZ) =
|
||||
* PWM_CLK / { (prescaler_value + 1) * (divider_value) }
|
||||
*/
|
||||
|
||||
val = readl(&pwm->tcfg0);
|
||||
if (pwm_id < 2) {
|
||||
prescaler = PRESCALER_0;
|
||||
val &= ~0xff;
|
||||
val |= (prescaler & 0xff);
|
||||
} else {
|
||||
prescaler = PRESCALER_1;
|
||||
val &= ~(0xff << 8);
|
||||
val |= (prescaler & 0xff) << 8;
|
||||
}
|
||||
writel(val, &pwm->tcfg0);
|
||||
val = readl(&pwm->tcfg1);
|
||||
val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
|
||||
val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
|
||||
writel(val, &pwm->tcfg1);
|
||||
|
||||
if (pwm_id == 4) {
|
||||
/*
|
||||
* TODO(sjg): Use this as a countdown timer for now. We count
|
||||
* down from the maximum value to 0, then reset.
|
||||
*/
|
||||
ticks_per_period = -1UL;
|
||||
} else {
|
||||
const unsigned long pwm_hz = 1000;
|
||||
unsigned long timer_rate_hz = get_pwm_clk() /
|
||||
((prescaler + 1) * (1 << div));
|
||||
|
||||
ticks_per_period = timer_rate_hz / pwm_hz;
|
||||
}
|
||||
|
||||
/* set count value */
|
||||
offset = pwm_id * 3;
|
||||
|
||||
writel(ticks_per_period, &pwm->tcntb0 + offset);
|
||||
|
||||
val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
|
||||
if (invert && (pwm_id < 4))
|
||||
val |= TCON_INVERTER(pwm_id);
|
||||
writel(val, &pwm->tcon);
|
||||
|
||||
pwm_enable(pwm_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Naveen Krishna Ch <ch.naveen@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/sromc.h>
|
||||
|
||||
/*
|
||||
* s5p_config_sromc() - select the proper SROMC Bank and configure the
|
||||
* band width control and bank control registers
|
||||
* srom_bank - SROM
|
||||
* srom_bw_conf - SMC Band witdh reg configuration value
|
||||
* srom_bc_conf - SMC Bank Control reg configuration value
|
||||
*/
|
||||
void s5p_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
|
||||
{
|
||||
u32 tmp;
|
||||
struct s5p_sromc *srom =
|
||||
(struct s5p_sromc *)samsung_get_base_sromc();
|
||||
|
||||
/* Configure SMC_BW register to handle proper SROMC bank */
|
||||
tmp = srom->bw;
|
||||
tmp &= ~(0xF << (srom_bank * 4));
|
||||
tmp |= srom_bw_conf;
|
||||
srom->bw = tmp;
|
||||
|
||||
/* Configure SMC_BC register */
|
||||
srom->bc[srom_bank] = srom_bc_conf;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Heungjun Kim <riverful.kim@samsung.com>
|
||||
* Inki Dae <inki.dae@samsung.com>
|
||||
* Minkyu Kang <mk7.kang@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <div64.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/pwm.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
/* Use the old PWM interface for now */
|
||||
#undef CONFIG_DM_PWM
|
||||
#include <pwm.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
unsigned long get_current_tick(void);
|
||||
static void reset_timer_masked(void);
|
||||
|
||||
/* macro to read the 16 bit timer */
|
||||
static inline struct s5p_timer *s5p_get_base_timer(void)
|
||||
{
|
||||
return (struct s5p_timer *)samsung_get_base_timer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the countdown timer.
|
||||
*
|
||||
* This operates at 1MHz and counts downwards. It will wrap about every
|
||||
* hour (2^32 microseconds).
|
||||
*
|
||||
* @return current value of timer
|
||||
*/
|
||||
static unsigned long timer_get_us_down(void)
|
||||
{
|
||||
struct s5p_timer *const timer = s5p_get_base_timer();
|
||||
|
||||
return readl(&timer->tcnto4);
|
||||
}
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
/* PWM Timer 4 */
|
||||
pwm_init(4, MUX_DIV_4, 0);
|
||||
pwm_config(4, 100000, 100000);
|
||||
pwm_enable(4);
|
||||
|
||||
/* Use this as the current monotonic time in us */
|
||||
gd->arch.timer_reset_value = 0;
|
||||
|
||||
/* Use this as the last timer value we saw */
|
||||
gd->arch.lastinc = timer_get_us_down();
|
||||
reset_timer_masked();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* timer without interrupts
|
||||
*/
|
||||
unsigned long get_timer(unsigned long base)
|
||||
{
|
||||
unsigned long long time_ms;
|
||||
|
||||
ulong now = timer_get_us_down();
|
||||
|
||||
/*
|
||||
* Increment the time by the amount elapsed since the last read.
|
||||
* The timer may have wrapped around, but it makes no difference to
|
||||
* our arithmetic here.
|
||||
*/
|
||||
gd->arch.timer_reset_value += gd->arch.lastinc - now;
|
||||
gd->arch.lastinc = now;
|
||||
|
||||
/* Divide by 1000 to convert from us to ms */
|
||||
time_ms = gd->arch.timer_reset_value;
|
||||
do_div(time_ms, 1000);
|
||||
return time_ms - base;
|
||||
}
|
||||
|
||||
unsigned long __attribute__((no_instrument_function)) timer_get_us(void)
|
||||
{
|
||||
static unsigned long base_time_us;
|
||||
|
||||
struct s5p_timer *const timer =
|
||||
(struct s5p_timer *)samsung_get_base_timer();
|
||||
unsigned long now_downward_us = readl(&timer->tcnto4);
|
||||
|
||||
if (!base_time_us)
|
||||
base_time_us = now_downward_us;
|
||||
|
||||
/* Note that this timer counts downward. */
|
||||
return base_time_us - now_downward_us;
|
||||
}
|
||||
|
||||
/* delay x useconds */
|
||||
void __udelay(unsigned long usec)
|
||||
{
|
||||
unsigned long count_value;
|
||||
|
||||
count_value = timer_get_us_down();
|
||||
while ((int)(count_value - timer_get_us_down()) < (int)usec)
|
||||
;
|
||||
}
|
||||
|
||||
static void reset_timer_masked(void)
|
||||
{
|
||||
struct s5p_timer *const timer = s5p_get_base_timer();
|
||||
|
||||
/* reset time */
|
||||
gd->arch.lastinc = readl(&timer->tcnto4);
|
||||
gd->arch.tbl = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (read timebase as long long).
|
||||
* On ARM it just returns the timer value.
|
||||
*/
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
return get_timer(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (timebase clock frequency).
|
||||
* On ARM it returns the number of timer ticks per second.
|
||||
*/
|
||||
unsigned long get_tbclk(void)
|
||||
{
|
||||
return CONFIG_SYS_HZ;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Routines to access the system control register
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
/*
|
||||
* void allow_unaligned(void) - allow unaligned access
|
||||
*
|
||||
* This routine clears the aligned flag in the system control register.
|
||||
* After calling this routine unaligned access does no longer lead to a
|
||||
* data abort but is handled by the CPU.
|
||||
*/
|
||||
ENTRY(allow_unaligned)
|
||||
mrc p15, 0, r0, c1, c0, 0 @ load system control register
|
||||
bic r0, r0, #2 @ clear aligned flag
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write system control register
|
||||
bx lr @ return
|
||||
ENDPROC(allow_unaligned)
|
||||
@@ -0,0 +1,59 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015, Linaro Limited
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/opcodes-sec.h>
|
||||
#include <asm/opcodes-virt.h>
|
||||
|
||||
#ifdef CONFIG_EFI_LOADER
|
||||
.section .text.efi_runtime
|
||||
#endif
|
||||
|
||||
#define UNWIND(x...)
|
||||
/*
|
||||
* Wrap c macros in asm macros to delay expansion until after the
|
||||
* SMCCC asm macro is expanded.
|
||||
*/
|
||||
.macro SMCCC_SMC
|
||||
__SMC(0)
|
||||
.endm
|
||||
|
||||
.macro SMCCC_HVC
|
||||
__HVC(0)
|
||||
.endm
|
||||
|
||||
.macro SMCCC instr
|
||||
UNWIND( .fnstart)
|
||||
mov r12, sp
|
||||
push {r4-r7}
|
||||
UNWIND( .save {r4-r7})
|
||||
ldm r12, {r4-r7}
|
||||
\instr
|
||||
pop {r4-r7}
|
||||
ldr r12, [sp, #(4 * 4)]
|
||||
stm r12, {r0-r3}
|
||||
bx lr
|
||||
UNWIND( .fnend)
|
||||
.endm
|
||||
|
||||
/*
|
||||
* void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
|
||||
* unsigned long a3, unsigned long a4, unsigned long a5,
|
||||
* unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
|
||||
* struct arm_smccc_quirk *quirk)
|
||||
*/
|
||||
ENTRY(__arm_smccc_smc)
|
||||
SMCCC SMCCC_SMC
|
||||
ENDPROC(__arm_smccc_smc)
|
||||
|
||||
/*
|
||||
* void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
|
||||
* unsigned long a3, unsigned long a4, unsigned long a5,
|
||||
* unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
|
||||
* struct arm_smccc_quirk *quirk)
|
||||
*/
|
||||
ENTRY(__arm_smccc_hvc)
|
||||
SMCCC SMCCC_HVC
|
||||
ENDPROC(__arm_smccc_hvc)
|
||||
@@ -0,0 +1,346 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* armboot - Startup Code for OMAP3530/ARM Cortex CPU-core
|
||||
*
|
||||
* Copyright (c) 2004 Texas Instruments <r-woodruff2@ti.com>
|
||||
*
|
||||
* Copyright (c) 2001 Marius Gröger <mag@sysgo.de>
|
||||
* Copyright (c) 2002 Alex Züpke <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) 2006-2008 Syed Mohammed Khasim <x0khasim@ti.com>
|
||||
*/
|
||||
|
||||
#include <asm-offsets.h>
|
||||
#include <config.h>
|
||||
#include <asm/system.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/armv7.h>
|
||||
|
||||
#include <linux/lotus/step.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
|
||||
.globl save_boot_params_ret
|
||||
.type save_boot_params_ret,%function
|
||||
#ifdef CONFIG_ARMV7_LPAE
|
||||
.global switch_to_hypervisor_ret
|
||||
#endif
|
||||
|
||||
reset:
|
||||
step_no_stack 50, r11, r12
|
||||
/* Allow the board to save important registers */
|
||||
b save_boot_params
|
||||
save_boot_params_ret:
|
||||
#ifdef CONFIG_ARMV7_LPAE
|
||||
/*
|
||||
* check for Hypervisor support
|
||||
*/
|
||||
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
|
||||
and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
|
||||
cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
|
||||
beq switch_to_hypervisor
|
||||
switch_to_hypervisor_ret:
|
||||
#endif
|
||||
/*
|
||||
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
|
||||
* except if in HYP mode already
|
||||
*/
|
||||
mrs r0, cpsr
|
||||
and r1, r0, #0x1f @ mask mode bits
|
||||
teq r1, #0x1a @ test for HYP mode
|
||||
bicne r0, r0, #0x1f @ clear all mode bits
|
||||
orrne r0, r0, #0x13 @ set SVC mode
|
||||
orr r0, r0, #0xc0 @ disable FIQ and IRQ
|
||||
msr cpsr,r0
|
||||
|
||||
/*
|
||||
* Setup vector:
|
||||
* (OMAP4 spl TEXT_BASE is not 32 byte aligned.
|
||||
* Continue to use ROM code vector only in OMAP4 spl)
|
||||
*/
|
||||
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
|
||||
/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
|
||||
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
|
||||
bic r0, #CR_V @ V = 0
|
||||
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
|
||||
|
||||
#ifdef CONFIG_HAS_VBAR
|
||||
/* Set vector address in CP15 VBAR register */
|
||||
ldr r0, =_start
|
||||
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* the mask ROM code should have PLL and others stable */
|
||||
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
|
||||
#ifdef CONFIG_CPU_V7A
|
||||
bl cpu_init_cp15
|
||||
#endif
|
||||
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
|
||||
bl cpu_init_crit
|
||||
#endif
|
||||
#endif
|
||||
|
||||
step_no_stack 51, r11, r12
|
||||
bl _main
|
||||
|
||||
/*------------------------------------------------------------------------------*/
|
||||
|
||||
ENTRY(c_runtime_cpu_setup)
|
||||
/*
|
||||
* If I-cache is enabled invalidate it
|
||||
*/
|
||||
#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
|
||||
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
|
||||
mcr p15, 0, r0, c7, c10, 4 @ DSB
|
||||
mcr p15, 0, r0, c7, c5, 4 @ ISB
|
||||
#endif
|
||||
|
||||
bx lr
|
||||
|
||||
ENDPROC(c_runtime_cpu_setup)
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
|
||||
* __attribute__((weak));
|
||||
*
|
||||
* Stack pointer is not yet initialized at this moment
|
||||
* Don't save anything to stack even if compiled with -O0
|
||||
*
|
||||
*************************************************************************/
|
||||
ENTRY(save_boot_params)
|
||||
b save_boot_params_ret @ back to my caller
|
||||
ENDPROC(save_boot_params)
|
||||
.weak save_boot_params
|
||||
|
||||
#ifdef CONFIG_ARMV7_LPAE
|
||||
ENTRY(switch_to_hypervisor)
|
||||
b switch_to_hypervisor_ret
|
||||
ENDPROC(switch_to_hypervisor)
|
||||
.weak switch_to_hypervisor
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* cpu_init_cp15
|
||||
*
|
||||
* Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
|
||||
* CONFIG_SYS_ICACHE_OFF is defined.
|
||||
*
|
||||
*************************************************************************/
|
||||
ENTRY(cpu_init_cp15)
|
||||
/*
|
||||
* Invalidate L1 I/D
|
||||
*/
|
||||
mov r0, #0 @ set up for MCR
|
||||
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
|
||||
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
|
||||
mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
|
||||
mcr p15, 0, r0, c7, c10, 4 @ DSB
|
||||
mcr p15, 0, r0, c7, c5, 4 @ ISB
|
||||
|
||||
/*
|
||||
* disable MMU stuff and caches
|
||||
*/
|
||||
mrc p15, 0, r0, c1, c0, 0
|
||||
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
|
||||
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
|
||||
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
|
||||
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
|
||||
#if CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
|
||||
bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
|
||||
#else
|
||||
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
|
||||
#endif
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_716044
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read system control register
|
||||
orr r0, r0, #1 << 11 @ set bit #11
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write system control register
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 4 @ set bit #4
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_743622
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 6 @ set bit #6
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_751472
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 11 @ set bit #11
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_ERRATA_761320
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 21 @ set bit #21
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_845369
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 22 @ set bit #22
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
|
||||
mov r5, lr @ Store my Caller
|
||||
mrc p15, 0, r1, c0, c0, 0 @ r1 has Read Main ID Register (MIDR)
|
||||
mov r3, r1, lsr #20 @ get variant field
|
||||
and r3, r3, #0xf @ r3 has CPU variant
|
||||
and r4, r1, #0xf @ r4 has CPU revision
|
||||
mov r2, r3, lsl #4 @ shift variant field for combined value
|
||||
orr r2, r4, r2 @ r2 has combined CPU variant + revision
|
||||
|
||||
/* Early stack for ERRATA that needs into call C code */
|
||||
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
|
||||
ldr r0, =(CONFIG_SPL_STACK)
|
||||
#else
|
||||
ldr r0, =(CONFIG_SYS_INIT_SP_ADDR)
|
||||
#endif
|
||||
bic r0, r0, #7 /* 8-byte alignment for ABI compliance */
|
||||
mov sp, r0
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_798870
|
||||
cmp r2, #0x30 @ Applies to lower than R3p0
|
||||
bge skip_errata_798870 @ skip if not affected rev
|
||||
cmp r2, #0x20 @ Applies to including and above R2p0
|
||||
blt skip_errata_798870 @ skip if not affected rev
|
||||
|
||||
mrc p15, 1, r0, c15, c0, 0 @ read l2 aux ctrl reg
|
||||
orr r0, r0, #1 << 7 @ Enable hazard-detect timeout
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_l2aux_ctrl
|
||||
isb @ Recommended ISB after l2actlr update
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
skip_errata_798870:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_801819
|
||||
cmp r2, #0x24 @ Applies to lt including R2p4
|
||||
bgt skip_errata_801819 @ skip if not affected rev
|
||||
cmp r2, #0x20 @ Applies to including and above R2p0
|
||||
blt skip_errata_801819 @ skip if not affected rev
|
||||
mrc p15, 0, r0, c0, c0, 6 @ pick up REVIDR reg
|
||||
and r0, r0, #1 << 3 @ check REVIDR[3]
|
||||
cmp r0, #1 << 3
|
||||
beq skip_errata_801819 @ skip erratum if REVIDR[3] is set
|
||||
|
||||
mrc p15, 0, r0, c1, c0, 1 @ read auxilary control register
|
||||
orr r0, r0, #3 << 27 @ Disables streaming. All write-allocate
|
||||
@ lines allocate in the L1 or L2 cache.
|
||||
orr r0, r0, #3 << 25 @ Disables streaming. All write-allocate
|
||||
@ lines allocate in the L1 cache.
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_acr
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
skip_errata_801819:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_CORTEX_A15_CVE_2017_5715
|
||||
mrc p15, 0, r0, c1, c0, 1 @ read auxilary control register
|
||||
orr r0, r0, #1 << 0 @ Enable invalidates of BTB
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_acr
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_454179
|
||||
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
|
||||
|
||||
cmp r2, #0x21 @ Only on < r2p1
|
||||
orrlt r0, r0, #(0x3 << 6) @ Set DBSM(BIT7) and IBE(BIT6) bits
|
||||
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_acr
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARM_ERRATA_430973) || defined (CONFIG_ARM_CORTEX_A8_CVE_2017_5715)
|
||||
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
|
||||
|
||||
#ifdef CONFIG_ARM_CORTEX_A8_CVE_2017_5715
|
||||
orr r0, r0, #(0x1 << 6) @ Set IBE bit always to enable OS WA
|
||||
#else
|
||||
cmp r2, #0x21 @ Only on < r2p1
|
||||
orrlt r0, r0, #(0x1 << 6) @ Set IBE bit
|
||||
#endif
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_acr
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_621766
|
||||
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
|
||||
|
||||
cmp r2, #0x21 @ Only on < r2p1
|
||||
orrlt r0, r0, #(0x1 << 5) @ Set L1NEON bit
|
||||
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_acr
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_725233
|
||||
mrc p15, 1, r0, c9, c0, 2 @ Read L2ACR
|
||||
|
||||
cmp r2, #0x21 @ Only on < r2p1 (Cortex A8)
|
||||
orrlt r0, r0, #(0x1 << 27) @ L2 PLD data forwarding disable
|
||||
|
||||
push {r1-r5} @ Save the cpu info registers
|
||||
bl v7_arch_cp15_set_l2aux_ctrl
|
||||
pop {r1-r5} @ Restore the cpu info - fall through
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_852421
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 24 @ set bit #24
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_852423
|
||||
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
|
||||
orr r0, r0, #1 << 12 @ set bit #12
|
||||
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
|
||||
#endif
|
||||
|
||||
mov pc, r5 @ back to my caller
|
||||
ENDPROC(cpu_init_cp15)
|
||||
|
||||
#if !defined(CONFIG_SKIP_LOWLEVEL_INIT) && \
|
||||
!defined(CONFIG_SKIP_LOWLEVEL_INIT_ONLY)
|
||||
/*************************************************************************
|
||||
*
|
||||
* CPU_init_critical registers
|
||||
*
|
||||
* setup important registers
|
||||
* setup memory timing
|
||||
*
|
||||
*************************************************************************/
|
||||
ENTRY(cpu_init_crit)
|
||||
/*
|
||||
* Jump to board specific initialization...
|
||||
* The Mask ROM will have already initialized
|
||||
* basic memory. Go here to bump up clock rate and handle
|
||||
* wake up conditions.
|
||||
*/
|
||||
b lowlevel_init @ go setup pll,mux,memory
|
||||
ENDPROC(cpu_init_crit)
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2014, STMicroelectronics - All Rights Reserved
|
||||
# Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
|
||||
|
||||
obj-y := timer.o clock.o pinmux.o reset.o
|
||||
obj-y += lowlevel.o
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, STMicroelectronics - All Rights Reserved
|
||||
* Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/stv0991_cgu.h>
|
||||
#include<asm/arch/stv0991_periph.h>
|
||||
|
||||
static struct stv0991_cgu_regs *const stv0991_cgu_regs = \
|
||||
(struct stv0991_cgu_regs *) (CGU_BASE_ADDR);
|
||||
|
||||
void enable_pll1(void)
|
||||
{
|
||||
/* pll1 already configured for 1000Mhz, just need to enable it */
|
||||
writel(readl(&stv0991_cgu_regs->pll1_ctrl) & ~(0x01),
|
||||
&stv0991_cgu_regs->pll1_ctrl);
|
||||
}
|
||||
|
||||
void clock_setup(int peripheral)
|
||||
{
|
||||
switch (peripheral) {
|
||||
case UART_CLOCK_CFG:
|
||||
writel(UART_CLK_CFG, &stv0991_cgu_regs->uart_freq);
|
||||
break;
|
||||
case ETH_CLOCK_CFG:
|
||||
enable_pll1();
|
||||
writel(ETH_CLK_CFG, &stv0991_cgu_regs->eth_freq);
|
||||
|
||||
/* Clock selection for ethernet tx_clk & rx_clk*/
|
||||
writel((readl(&stv0991_cgu_regs->eth_ctrl) & ETH_CLK_MASK)
|
||||
| ETH_CLK_CTRL, &stv0991_cgu_regs->eth_ctrl);
|
||||
break;
|
||||
case QSPI_CLOCK_CFG:
|
||||
writel(QSPI_CLK_CTRL, &stv0991_cgu_regs->qspi_freq);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2014 stmicroelectronics
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
ENTRY(lowlevel_init)
|
||||
mov pc, lr
|
||||
ENDPROC(lowlevel_init)
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, STMicroelectronics - All Rights Reserved
|
||||
* Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/stv0991_creg.h>
|
||||
#include <asm/arch/stv0991_periph.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
static struct stv0991_creg *const stv0991_creg = \
|
||||
(struct stv0991_creg *)CREG_BASE_ADDR;
|
||||
|
||||
int stv0991_pinmux_config(int peripheral)
|
||||
{
|
||||
switch (peripheral) {
|
||||
case UART_GPIOC_30_31:
|
||||
/* SSDA/SSCL pad muxing to UART Rx/Dx */
|
||||
writel((readl(&stv0991_creg->mux12) & GPIOC_31_MUX_MASK) |
|
||||
CFG_GPIOC_31_UART_RX,
|
||||
&stv0991_creg->mux12);
|
||||
writel((readl(&stv0991_creg->mux12) & GPIOC_30_MUX_MASK) |
|
||||
CFG_GPIOC_30_UART_TX,
|
||||
&stv0991_creg->mux12);
|
||||
/* SSDA/SSCL pad config to push pull*/
|
||||
writel((readl(&stv0991_creg->cfg_pad6) & GPIOC_31_MODE_MASK) |
|
||||
CFG_GPIOC_31_MODE_PP,
|
||||
&stv0991_creg->cfg_pad6);
|
||||
writel((readl(&stv0991_creg->cfg_pad6) & GPIOC_30_MODE_MASK) |
|
||||
CFG_GPIOC_30_MODE_HIGH,
|
||||
&stv0991_creg->cfg_pad6);
|
||||
break;
|
||||
case UART_GPIOB_16_17:
|
||||
/* ethernet rx_6/7 to UART Rx/Dx */
|
||||
writel((readl(&stv0991_creg->mux7) & GPIOB_17_MUX_MASK) |
|
||||
CFG_GPIOB_17_UART_RX,
|
||||
&stv0991_creg->mux7);
|
||||
writel((readl(&stv0991_creg->mux7) & GPIOB_16_MUX_MASK) |
|
||||
CFG_GPIOB_16_UART_TX,
|
||||
&stv0991_creg->mux7);
|
||||
break;
|
||||
case ETH_GPIOB_10_31_C_0_4:
|
||||
writel(readl(&stv0991_creg->mux6) & 0x000000FF,
|
||||
&stv0991_creg->mux6);
|
||||
writel(0x00000000, &stv0991_creg->mux7);
|
||||
writel(0x00000000, &stv0991_creg->mux8);
|
||||
writel(readl(&stv0991_creg->mux9) & 0xFFF00000,
|
||||
&stv0991_creg->mux9);
|
||||
/* Ethernet Voltage configuration to 1.8V*/
|
||||
writel((readl(&stv0991_creg->vdd_pad1) & VDD_ETH_PS_MASK) |
|
||||
ETH_VDD_CFG, &stv0991_creg->vdd_pad1);
|
||||
writel((readl(&stv0991_creg->vdd_pad1) & VDD_ETH_PS_MASK) |
|
||||
ETH_M_VDD_CFG, &stv0991_creg->vdd_pad1);
|
||||
|
||||
break;
|
||||
case QSPI_CS_CLK_PAD:
|
||||
writel((readl(&stv0991_creg->mux13) & FLASH_CS_NC_MASK) |
|
||||
CFG_FLASH_CS_NC, &stv0991_creg->mux13);
|
||||
writel((readl(&stv0991_creg->mux13) & FLASH_CLK_MASK) |
|
||||
CFG_FLASH_CLK, &stv0991_creg->mux13);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, STMicroelectronics - All Rights Reserved
|
||||
* Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/stv0991_wdru.h>
|
||||
void reset_cpu(ulong ignored)
|
||||
{
|
||||
puts("System is going to reboot ...\n");
|
||||
/*
|
||||
* This 1 second delay will allow the above message
|
||||
* to be printed before reset
|
||||
*/
|
||||
udelay((1000 * 1000));
|
||||
|
||||
/* Setting bit 1 of the WDRU unit will reset the SoC */
|
||||
writel(WDRU_RST_SYS, &stv0991_wd_ru_ptr->wdru_ctrl1);
|
||||
|
||||
/* system will restart */
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, STMicroelectronics - All Rights Reserved
|
||||
* Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-stv0991/hardware.h>
|
||||
#include <asm/arch-stv0991/stv0991_cgu.h>
|
||||
#include <asm/arch-stv0991/stv0991_gpt.h>
|
||||
|
||||
static struct stv0991_cgu_regs *const stv0991_cgu_regs = \
|
||||
(struct stv0991_cgu_regs *) (CGU_BASE_ADDR);
|
||||
|
||||
#define READ_TIMER() (readl(&gpt1_regs_ptr->cnt) & GPT_FREE_RUNNING)
|
||||
#define GPT_RESOLUTION (CONFIG_STV0991_HZ_CLOCK / CONFIG_STV0991_HZ)
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define timestamp gd->arch.tbl
|
||||
#define lastdec gd->arch.lastinc
|
||||
|
||||
static ulong get_timer_masked(void);
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
/* Timer1 clock configuration */
|
||||
writel(TIMER1_CLK_CFG, &stv0991_cgu_regs->tim_freq);
|
||||
writel(readl(&stv0991_cgu_regs->cgu_enable_2) |
|
||||
TIMER1_CLK_EN, &stv0991_cgu_regs->cgu_enable_2);
|
||||
|
||||
/* Stop the timer */
|
||||
writel(readl(&gpt1_regs_ptr->cr1) & ~GPT_CR1_CEN, &gpt1_regs_ptr->cr1);
|
||||
writel(GPT_PRESCALER_128, &gpt1_regs_ptr->psc);
|
||||
/* Configure timer for auto-reload */
|
||||
writel(readl(&gpt1_regs_ptr->cr1) | GPT_MODE_AUTO_RELOAD,
|
||||
&gpt1_regs_ptr->cr1);
|
||||
|
||||
/* load value for free running */
|
||||
writel(GPT_FREE_RUNNING, &gpt1_regs_ptr->arr);
|
||||
|
||||
/* start timer */
|
||||
writel(readl(&gpt1_regs_ptr->cr1) | GPT_CR1_CEN,
|
||||
&gpt1_regs_ptr->cr1);
|
||||
|
||||
/* Reset the timer */
|
||||
lastdec = READ_TIMER();
|
||||
timestamp = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* timer without interrupts
|
||||
*/
|
||||
ulong get_timer(ulong base)
|
||||
{
|
||||
return (get_timer_masked() / GPT_RESOLUTION) - base;
|
||||
}
|
||||
|
||||
void __udelay(unsigned long usec)
|
||||
{
|
||||
ulong tmo;
|
||||
ulong start = get_timer_masked();
|
||||
ulong tenudelcnt = CONFIG_STV0991_HZ_CLOCK / (1000 * 100);
|
||||
ulong rndoff;
|
||||
|
||||
rndoff = (usec % 10) ? 1 : 0;
|
||||
|
||||
/* tenudelcnt timer tick gives 10 microsecconds delay */
|
||||
tmo = ((usec / 10) + rndoff) * tenudelcnt;
|
||||
|
||||
while ((ulong) (get_timer_masked() - start) < tmo)
|
||||
;
|
||||
}
|
||||
|
||||
static ulong get_timer_masked(void)
|
||||
{
|
||||
ulong now = READ_TIMER();
|
||||
|
||||
if (now >= lastdec) {
|
||||
/* normal mode */
|
||||
timestamp += now - lastdec;
|
||||
} else {
|
||||
/* we have an overflow ... */
|
||||
timestamp += now + GPT_FREE_RUNNING - lastdec;
|
||||
}
|
||||
lastdec = now;
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (read timebase as long long).
|
||||
* On ARM it just returns the timer value.
|
||||
*/
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
return get_timer(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (timebase clock frequency).
|
||||
* On ARM it returns the number of timer ticks per second.
|
||||
*/
|
||||
ulong get_tbclk(void)
|
||||
{
|
||||
return CONFIG_STV0991_HZ;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
#
|
||||
# Based on some other Makefile
|
||||
# (C) Copyright 2000-2003
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
obj-y += timer.o
|
||||
|
||||
obj-$(CONFIG_MACH_SUN6I) += tzpc.o
|
||||
obj-$(CONFIG_MACH_SUN8I_H3) += tzpc.o
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_ARMV7_PSCI) += psci.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
obj-y += fel_utils.o
|
||||
endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Utility functions for FEL mode.
|
||||
*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
*/
|
||||
|
||||
#include <asm-offsets.h>
|
||||
#include <config.h>
|
||||
#include <asm/system.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
ENTRY(save_boot_params)
|
||||
ldr r0, =fel_stash
|
||||
str sp, [r0, #0]
|
||||
str lr, [r0, #4]
|
||||
mrs lr, cpsr @ Read CPSR
|
||||
str lr, [r0, #8]
|
||||
mrc p15, 0, lr, c1, c0, 0 @ Read CP15 SCTLR Register
|
||||
str lr, [r0, #12]
|
||||
mrc p15, 0, lr, c12, c0, 0 @ Read VBAR
|
||||
str lr, [r0, #16]
|
||||
mrc p15, 0, lr, c1, c0, 0 @ Read CP15 Control Register
|
||||
str lr, [r0, #20]
|
||||
b save_boot_params_ret
|
||||
ENDPROC(save_boot_params)
|
||||
|
||||
ENTRY(return_to_fel)
|
||||
mov sp, r0
|
||||
mov lr, r1
|
||||
ldr r0, =fel_stash
|
||||
ldr r1, [r0, #20]
|
||||
mcr p15, 0, r1, c1, c0, 0 @ Write CP15 Control Register
|
||||
ldr r1, [r0, #16]
|
||||
mcr p15, 0, r1, c12, c0, 0 @ Write VBAR
|
||||
ldr r1, [r0, #12]
|
||||
mcr p15, 0, r1, c1, c0, 0 @ Write CP15 SCTLR Register
|
||||
ldr r1, [r0, #8]
|
||||
msr cpsr, r1 @ Write CPSR
|
||||
bx lr
|
||||
ENDPROC(return_to_fel)
|
||||
@@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2016
|
||||
* Author: Chen-Yu Tsai <wens@csie.org>
|
||||
*
|
||||
* Based on assembly code by Marc Zyngier <marc.zyngier@arm.com>,
|
||||
* which was based on code by Carl van Schaik <carl@ok-labs.com>.
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/cpucfg.h>
|
||||
#include <asm/arch/prcm.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/gic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/psci.h>
|
||||
#include <asm/secure.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define __irq __attribute__ ((interrupt ("IRQ")))
|
||||
|
||||
#define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
|
||||
#define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
|
||||
|
||||
/*
|
||||
* R40 is different from other single cluster SoCs.
|
||||
*
|
||||
* The power clamps are located in the unused space after the per-core
|
||||
* reset controls for core 3. The secondary core entry address register
|
||||
* is in the SRAM controller address range.
|
||||
*/
|
||||
#define SUN8I_R40_PWROFF (0x110)
|
||||
#define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4)
|
||||
#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
|
||||
|
||||
static void __secure cp15_write_cntp_tval(u32 tval)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
|
||||
}
|
||||
|
||||
static void __secure cp15_write_cntp_ctl(u32 val)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
|
||||
}
|
||||
|
||||
static u32 __secure cp15_read_cntp_ctl(void)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define ONE_MS (COUNTER_FREQUENCY / 1000)
|
||||
|
||||
static void __secure __mdelay(u32 ms)
|
||||
{
|
||||
u32 reg = ONE_MS * ms;
|
||||
|
||||
cp15_write_cntp_tval(reg);
|
||||
isb();
|
||||
cp15_write_cntp_ctl(3);
|
||||
|
||||
do {
|
||||
isb();
|
||||
reg = cp15_read_cntp_ctl();
|
||||
} while (!(reg & BIT(2)));
|
||||
|
||||
cp15_write_cntp_ctl(0);
|
||||
isb();
|
||||
}
|
||||
|
||||
static void __secure clamp_release(void __maybe_unused *clamp)
|
||||
{
|
||||
#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
|
||||
defined(CONFIG_MACH_SUN8I_H3) || \
|
||||
defined(CONFIG_MACH_SUN8I_R40)
|
||||
u32 tmp = 0x1ff;
|
||||
do {
|
||||
tmp >>= 1;
|
||||
writel(tmp, clamp);
|
||||
} while (tmp);
|
||||
|
||||
__mdelay(10);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __secure clamp_set(void __maybe_unused *clamp)
|
||||
{
|
||||
#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
|
||||
defined(CONFIG_MACH_SUN8I_H3) || \
|
||||
defined(CONFIG_MACH_SUN8I_R40)
|
||||
writel(0xff, clamp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __secure sunxi_power_switch(void *clamp, void *pwroff_ptr, bool on,
|
||||
int cpu)
|
||||
{
|
||||
u32 pwroff;
|
||||
|
||||
memcpy(&pwroff, pwroff_ptr, sizeof(u32));
|
||||
|
||||
if (on) {
|
||||
/* Release power clamp */
|
||||
clamp_release(clamp);
|
||||
|
||||
/* Clear power gating */
|
||||
clrbits_le32(&pwroff, BIT(cpu));
|
||||
} else {
|
||||
/* Set power gating */
|
||||
setbits_le32(&pwroff, BIT(cpu));
|
||||
|
||||
/* Activate power clamp */
|
||||
clamp_set(clamp);
|
||||
}
|
||||
|
||||
memcpy(pwroff_ptr, &pwroff, sizeof(u32));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MACH_SUN8I_R40
|
||||
/* secondary core entry address is programmed differently on R40 */
|
||||
static void __secure sunxi_set_entry_address(void *entry)
|
||||
{
|
||||
writel((u32)entry,
|
||||
SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
|
||||
}
|
||||
#else
|
||||
static void __secure sunxi_set_entry_address(void *entry)
|
||||
{
|
||||
struct sunxi_cpucfg_reg *cpucfg =
|
||||
(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
|
||||
|
||||
writel((u32)entry, &cpucfg->priv0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MACH_SUN7I
|
||||
/* sun7i (A20) is different from other single cluster SoCs */
|
||||
static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on)
|
||||
{
|
||||
struct sunxi_cpucfg_reg *cpucfg =
|
||||
(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
|
||||
|
||||
sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
|
||||
on, 0);
|
||||
}
|
||||
#elif defined CONFIG_MACH_SUN8I_R40
|
||||
static void __secure sunxi_cpu_set_power(int cpu, bool on)
|
||||
{
|
||||
struct sunxi_cpucfg_reg *cpucfg =
|
||||
(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
|
||||
|
||||
sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu),
|
||||
(void *)cpucfg + SUN8I_R40_PWROFF,
|
||||
on, 0);
|
||||
}
|
||||
#else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */
|
||||
static void __secure sunxi_cpu_set_power(int cpu, bool on)
|
||||
{
|
||||
struct sunxi_prcm_reg *prcm =
|
||||
(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
|
||||
|
||||
sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
|
||||
on, cpu);
|
||||
}
|
||||
#endif /* CONFIG_MACH_SUN7I */
|
||||
|
||||
void __secure sunxi_cpu_power_off(u32 cpuid)
|
||||
{
|
||||
struct sunxi_cpucfg_reg *cpucfg =
|
||||
(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
|
||||
u32 cpu = cpuid & 0x3;
|
||||
|
||||
/* Wait for the core to enter WFI */
|
||||
while (1) {
|
||||
if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
|
||||
break;
|
||||
__mdelay(1);
|
||||
}
|
||||
|
||||
/* Assert reset on target CPU */
|
||||
writel(0, &cpucfg->cpu[cpu].rst);
|
||||
|
||||
/* Lock CPU (Disable external debug access) */
|
||||
clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
|
||||
|
||||
/* Power down CPU */
|
||||
sunxi_cpu_set_power(cpuid, false);
|
||||
|
||||
/* Unlock CPU (Disable external debug access) */
|
||||
setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
|
||||
}
|
||||
|
||||
static u32 __secure cp15_read_scr(void)
|
||||
{
|
||||
u32 scr;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
|
||||
|
||||
return scr;
|
||||
}
|
||||
|
||||
static void __secure cp15_write_scr(u32 scr)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
|
||||
isb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Although this is an FIQ handler, the FIQ is processed in monitor mode,
|
||||
* which means there's no FIQ banked registers. This is the same as IRQ
|
||||
* mode, so use the IRQ attribute to ask the compiler to handler entry
|
||||
* and return.
|
||||
*/
|
||||
void __secure __irq psci_fiq_enter(void)
|
||||
{
|
||||
u32 scr, reg, cpu;
|
||||
|
||||
/* Switch to secure mode */
|
||||
scr = cp15_read_scr();
|
||||
cp15_write_scr(scr & ~BIT(0));
|
||||
|
||||
/* Validate reason based on IAR and acknowledge */
|
||||
reg = readl(GICC_BASE + GICC_IAR);
|
||||
|
||||
/* Skip spurious interrupts 1022 and 1023 */
|
||||
if (reg == 1023 || reg == 1022)
|
||||
goto out;
|
||||
|
||||
/* End of interrupt */
|
||||
writel(reg, GICC_BASE + GICC_EOIR);
|
||||
dsb();
|
||||
|
||||
/* Get CPU number */
|
||||
cpu = (reg >> 10) & 0x7;
|
||||
|
||||
/* Power off the CPU */
|
||||
sunxi_cpu_power_off(cpu);
|
||||
|
||||
out:
|
||||
/* Restore security level */
|
||||
cp15_write_scr(scr);
|
||||
}
|
||||
|
||||
int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
|
||||
u32 context_id)
|
||||
{
|
||||
struct sunxi_cpucfg_reg *cpucfg =
|
||||
(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
|
||||
u32 cpu = (mpidr & 0x3);
|
||||
|
||||
/* store target PC and context id */
|
||||
psci_save(cpu, pc, context_id);
|
||||
|
||||
/* Set secondary core power on PC */
|
||||
sunxi_set_entry_address(&psci_cpu_entry);
|
||||
|
||||
/* Assert reset on target CPU */
|
||||
writel(0, &cpucfg->cpu[cpu].rst);
|
||||
|
||||
/* Invalidate L1 cache */
|
||||
clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
|
||||
|
||||
/* Lock CPU (Disable external debug access) */
|
||||
clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
|
||||
|
||||
/* Power up target CPU */
|
||||
sunxi_cpu_set_power(cpu, true);
|
||||
|
||||
/* De-assert reset on target CPU */
|
||||
writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
|
||||
|
||||
/* Unlock CPU (Disable external debug access) */
|
||||
setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
|
||||
|
||||
return ARM_PSCI_RET_SUCCESS;
|
||||
}
|
||||
|
||||
s32 __secure psci_cpu_off(void)
|
||||
{
|
||||
psci_cpu_off_common();
|
||||
|
||||
/* Ask CPU0 via SGI15 to pull the rug... */
|
||||
writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
|
||||
dsb();
|
||||
|
||||
/* Wait to be turned off */
|
||||
while (1)
|
||||
wfi();
|
||||
}
|
||||
|
||||
void __secure psci_arch_init(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* SGI15 as Group-0 */
|
||||
clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
|
||||
|
||||
/* Set SGI15 priority to 0 */
|
||||
writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
|
||||
|
||||
/* Be cool with non-secure */
|
||||
writel(0xff, GICC_BASE + GICC_PMR);
|
||||
|
||||
/* Switch FIQEn on */
|
||||
setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
|
||||
|
||||
reg = cp15_read_scr();
|
||||
reg |= BIT(2); /* Enable FIQ in monitor mode */
|
||||
reg &= ~BIT(0); /* Secure mode */
|
||||
cp15_write_scr(reg);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* Intentionally empty. Only needed to get FEL SPL link line right */
|
||||
@@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2007-2011
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
* Tom Cubie <tangliang@allwinnertech.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/timer.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define TIMER_MODE (0x0 << 7) /* continuous mode */
|
||||
#define TIMER_DIV (0x0 << 4) /* pre scale 1 */
|
||||
#define TIMER_SRC (0x1 << 2) /* osc24m */
|
||||
#define TIMER_RELOAD (0x1 << 1) /* reload internal value */
|
||||
#define TIMER_EN (0x1 << 0) /* enable timer */
|
||||
|
||||
#define TIMER_CLOCK (24 * 1000 * 1000)
|
||||
#define COUNT_TO_USEC(x) ((x) / 24)
|
||||
#define USEC_TO_COUNT(x) ((x) * 24)
|
||||
#define TICKS_PER_HZ (TIMER_CLOCK / CONFIG_SYS_HZ)
|
||||
#define TICKS_TO_HZ(x) ((x) / TICKS_PER_HZ)
|
||||
|
||||
#define TIMER_LOAD_VAL 0xffffffff
|
||||
|
||||
#define TIMER_NUM 0 /* we use timer 0 */
|
||||
|
||||
/* read the 32-bit timer */
|
||||
static ulong read_timer(void)
|
||||
{
|
||||
struct sunxi_timer_reg *timers =
|
||||
(struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
|
||||
struct sunxi_timer *timer = &timers->timer[TIMER_NUM];
|
||||
|
||||
/*
|
||||
* The hardware timer counts down, therefore we invert to
|
||||
* produce an incrementing timer.
|
||||
*/
|
||||
return ~readl(&timer->val);
|
||||
}
|
||||
|
||||
/* init timer register */
|
||||
int timer_init(void)
|
||||
{
|
||||
struct sunxi_timer_reg *timers =
|
||||
(struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
|
||||
struct sunxi_timer *timer = &timers->timer[TIMER_NUM];
|
||||
writel(TIMER_LOAD_VAL, &timer->inter);
|
||||
writel(TIMER_MODE | TIMER_DIV | TIMER_SRC | TIMER_RELOAD | TIMER_EN,
|
||||
&timer->ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* timer without interrupts */
|
||||
static ulong get_timer_masked(void)
|
||||
{
|
||||
/* current tick value */
|
||||
ulong now = TICKS_TO_HZ(read_timer());
|
||||
|
||||
if (now >= gd->arch.lastinc) /* normal (non rollover) */
|
||||
gd->arch.tbl += (now - gd->arch.lastinc);
|
||||
else {
|
||||
/* rollover */
|
||||
gd->arch.tbl += (TICKS_TO_HZ(TIMER_LOAD_VAL)
|
||||
- gd->arch.lastinc) + now;
|
||||
}
|
||||
gd->arch.lastinc = now;
|
||||
|
||||
return gd->arch.tbl;
|
||||
}
|
||||
|
||||
ulong get_timer(ulong base)
|
||||
{
|
||||
return get_timer_masked() - base;
|
||||
}
|
||||
|
||||
/* delay x useconds */
|
||||
void __udelay(unsigned long usec)
|
||||
{
|
||||
long tmo = USEC_TO_COUNT(usec);
|
||||
ulong now, last = read_timer();
|
||||
|
||||
while (tmo > 0) {
|
||||
now = read_timer();
|
||||
if (now > last) /* normal (non rollover) */
|
||||
tmo -= now - last;
|
||||
else /* rollover */
|
||||
tmo -= TIMER_LOAD_VAL - last + now;
|
||||
last = now;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (read timebase as long long).
|
||||
* On ARM it just returns the timer value.
|
||||
*/
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
return get_timer(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (timebase clock frequency).
|
||||
* On ARM it returns the number of timer ticks per second.
|
||||
*/
|
||||
ulong get_tbclk(void)
|
||||
{
|
||||
return CONFIG_SYS_HZ;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2015 Chen-Yu Tsai <wens@csie.org>
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/tzpc.h>
|
||||
|
||||
/* Configure Trust Zone Protection Controller */
|
||||
void tzpc_init(void)
|
||||
{
|
||||
struct sunxi_tzpc *tzpc = (struct sunxi_tzpc *)SUNXI_TZPC_BASE;
|
||||
|
||||
#ifdef CONFIG_MACH_SUN6I
|
||||
/* Enable non-secure access to the RTC */
|
||||
writel(SUN6I_TZPC_DECPORT0_RTC, &tzpc->decport0_set);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MACH_SUN8I_H3
|
||||
/* Enable non-secure access to all peripherals */
|
||||
writel(SUN8I_H3_TZPC_DECPORT0_ALL, &tzpc->decport0_set);
|
||||
writel(SUN8I_H3_TZPC_DECPORT1_ALL, &tzpc->decport1_set);
|
||||
writel(SUN8I_H3_TZPC_DECPORT2_ALL, &tzpc->decport2_set);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2012
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
* Tom Cubie <tangliang@allwinnertech.com>
|
||||
*
|
||||
* Based on omap-common/u-boot-spl.lds:
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
|
||||
*
|
||||
* (C) Copyright 2010
|
||||
* Texas Instruments, <www.ti.com>
|
||||
* Aneesh V <aneesh@ti.com>
|
||||
*/
|
||||
MEMORY { .sram : ORIGIN = IMAGE_TEXT_BASE,\
|
||||
LENGTH = IMAGE_MAX_SIZE }
|
||||
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \
|
||||
LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
|
||||
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
__start = .;
|
||||
*(.vectors)
|
||||
arch/arm/cpu/armv7/start.o (.text)
|
||||
*(.text*)
|
||||
} > .sram
|
||||
|
||||
. = ALIGN(4);
|
||||
.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
|
||||
|
||||
. = ALIGN(4);
|
||||
.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
|
||||
|
||||
. = ALIGN(4);
|
||||
.u_boot_list : {
|
||||
KEEP(*(SORT(.u_boot_list*)));
|
||||
} > .sram
|
||||
|
||||
. = ALIGN(4);
|
||||
__image_copy_end = .;
|
||||
_end = .;
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__bss_start = .;
|
||||
*(.bss*)
|
||||
. = ALIGN(4);
|
||||
__bss_end = .;
|
||||
} > .sdram
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2008
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Richard Woodruff <r-woodruff2@ti.com>
|
||||
* Syed Mohammed Khasim <khasim@ti.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/************************************************************
|
||||
* sdelay() - simple spin loop. Will be constant time as
|
||||
* its generally used in bypass conditions only. This
|
||||
* is necessary until timers are accessible.
|
||||
*
|
||||
* not inline to increase chances its in cache when called
|
||||
*************************************************************/
|
||||
void sdelay(unsigned long loops)
|
||||
{
|
||||
__asm__ volatile ("1:\n" "subs %0, %1, #1\n"
|
||||
"bne 1b":"=r" (loops):"0"(loops));
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* wait_on_value() - common routine to allow waiting for changes in
|
||||
* volatile regs.
|
||||
*********************************************************************/
|
||||
u32 wait_on_value(u32 read_bit_mask, u32 match_value, void *read_addr,
|
||||
u32 bound)
|
||||
{
|
||||
u32 i = 0, val;
|
||||
do {
|
||||
++i;
|
||||
val = readl((u32)read_addr) & read_bit_mask;
|
||||
if (val == match_value)
|
||||
return 1;
|
||||
if (i == bound)
|
||||
return 0;
|
||||
} while (1);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
if ARCH_VF610
|
||||
|
||||
config VF610
|
||||
bool
|
||||
default y
|
||||
|
||||
choice
|
||||
prompt "Vybrid board select"
|
||||
|
||||
config TARGET_VF610TWR
|
||||
bool "TWR-VF65GS10-DS5"
|
||||
|
||||
config TARGET_COLIBRI_VF
|
||||
bool "Colibri VF50/61"
|
||||
select BOARD_LATE_INIT
|
||||
|
||||
config TARGET_PCM052
|
||||
bool "PCM-052"
|
||||
select SYS_FSL_ERRATUM_ESDHC135
|
||||
select SYS_FSL_ERRATUM_ESDHC_A001
|
||||
|
||||
config TARGET_BK4R1
|
||||
bool "BK4r1"
|
||||
select SYS_FSL_ERRATUM_ESDHC135
|
||||
select SYS_FSL_ERRATUM_ESDHC_A001
|
||||
select BOARD_LATE_INIT
|
||||
|
||||
endchoice
|
||||
|
||||
config SYS_SOC
|
||||
default "vf610"
|
||||
|
||||
source "board/freescale/vf610twr/Kconfig"
|
||||
source "board/phytec/pcm052/Kconfig"
|
||||
source "board/toradex/colibri_vf/Kconfig"
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2013 Freescale Semiconductor, Inc.
|
||||
|
||||
obj-y += generic.o
|
||||
obj-y += timer.o
|
||||
|
||||
MKIMAGEFLAGS_u-boot.vyb = -T vybridimage
|
||||
|
||||
u-boot.vyb: u-boot.imx
|
||||
$(call if_changed,mkimage)
|
||||
@@ -0,0 +1,401 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/crm_regs.h>
|
||||
#include <asm/mach-imx/sys_proto.h>
|
||||
#include <env.h>
|
||||
#include <netdev.h>
|
||||
#ifdef CONFIG_FSL_ESDHC_IMX
|
||||
#include <fsl_esdhc_imx.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSL_ESDHC_IMX
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
#endif
|
||||
|
||||
static char soc_type[] = "xx0";
|
||||
|
||||
#ifdef CONFIG_MXC_OCOTP
|
||||
void enable_ocotp_clk(unsigned char enable)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(&ccm->ccgr6);
|
||||
if (enable)
|
||||
reg |= CCM_CCGR6_OCOTP_CTRL_MASK;
|
||||
else
|
||||
reg &= ~CCM_CCGR6_OCOTP_CTRL_MASK;
|
||||
writel(reg, &ccm->ccgr6);
|
||||
}
|
||||
#endif
|
||||
|
||||
static u32 get_mcu_main_clk(void)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
u32 ccm_ccsr, ccm_cacrr, armclk_div;
|
||||
u32 sysclk_sel, pll_pfd_sel = 0;
|
||||
u32 freq = 0;
|
||||
|
||||
ccm_ccsr = readl(&ccm->ccsr);
|
||||
sysclk_sel = ccm_ccsr & CCM_CCSR_SYS_CLK_SEL_MASK;
|
||||
sysclk_sel >>= CCM_CCSR_SYS_CLK_SEL_OFFSET;
|
||||
|
||||
ccm_cacrr = readl(&ccm->cacrr);
|
||||
armclk_div = ccm_cacrr & CCM_CACRR_ARM_CLK_DIV_MASK;
|
||||
armclk_div >>= CCM_CACRR_ARM_CLK_DIV_OFFSET;
|
||||
armclk_div += 1;
|
||||
|
||||
switch (sysclk_sel) {
|
||||
case 0:
|
||||
freq = FASE_CLK_FREQ;
|
||||
break;
|
||||
case 1:
|
||||
freq = SLOW_CLK_FREQ;
|
||||
break;
|
||||
case 2:
|
||||
pll_pfd_sel = ccm_ccsr & CCM_CCSR_PLL2_PFD_CLK_SEL_MASK;
|
||||
pll_pfd_sel >>= CCM_CCSR_PLL2_PFD_CLK_SEL_OFFSET;
|
||||
if (pll_pfd_sel == 0)
|
||||
freq = PLL2_MAIN_FREQ;
|
||||
else if (pll_pfd_sel == 1)
|
||||
freq = PLL2_PFD1_FREQ;
|
||||
else if (pll_pfd_sel == 2)
|
||||
freq = PLL2_PFD2_FREQ;
|
||||
else if (pll_pfd_sel == 3)
|
||||
freq = PLL2_PFD3_FREQ;
|
||||
else if (pll_pfd_sel == 4)
|
||||
freq = PLL2_PFD4_FREQ;
|
||||
break;
|
||||
case 3:
|
||||
freq = PLL2_MAIN_FREQ;
|
||||
break;
|
||||
case 4:
|
||||
pll_pfd_sel = ccm_ccsr & CCM_CCSR_PLL1_PFD_CLK_SEL_MASK;
|
||||
pll_pfd_sel >>= CCM_CCSR_PLL1_PFD_CLK_SEL_OFFSET;
|
||||
if (pll_pfd_sel == 0)
|
||||
freq = PLL1_MAIN_FREQ;
|
||||
else if (pll_pfd_sel == 1)
|
||||
freq = PLL1_PFD1_FREQ;
|
||||
else if (pll_pfd_sel == 2)
|
||||
freq = PLL1_PFD2_FREQ;
|
||||
else if (pll_pfd_sel == 3)
|
||||
freq = PLL1_PFD3_FREQ;
|
||||
else if (pll_pfd_sel == 4)
|
||||
freq = PLL1_PFD4_FREQ;
|
||||
break;
|
||||
case 5:
|
||||
freq = PLL3_MAIN_FREQ;
|
||||
break;
|
||||
default:
|
||||
printf("unsupported system clock select\n");
|
||||
}
|
||||
|
||||
return freq / armclk_div;
|
||||
}
|
||||
|
||||
static u32 get_bus_clk(void)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
u32 ccm_cacrr, busclk_div;
|
||||
|
||||
ccm_cacrr = readl(&ccm->cacrr);
|
||||
|
||||
busclk_div = ccm_cacrr & CCM_CACRR_BUS_CLK_DIV_MASK;
|
||||
busclk_div >>= CCM_CACRR_BUS_CLK_DIV_OFFSET;
|
||||
busclk_div += 1;
|
||||
|
||||
return get_mcu_main_clk() / busclk_div;
|
||||
}
|
||||
|
||||
static u32 get_ipg_clk(void)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
u32 ccm_cacrr, ipgclk_div;
|
||||
|
||||
ccm_cacrr = readl(&ccm->cacrr);
|
||||
|
||||
ipgclk_div = ccm_cacrr & CCM_CACRR_IPG_CLK_DIV_MASK;
|
||||
ipgclk_div >>= CCM_CACRR_IPG_CLK_DIV_OFFSET;
|
||||
ipgclk_div += 1;
|
||||
|
||||
return get_bus_clk() / ipgclk_div;
|
||||
}
|
||||
|
||||
static u32 get_uart_clk(void)
|
||||
{
|
||||
return get_ipg_clk();
|
||||
}
|
||||
|
||||
static u32 get_sdhc_clk(void)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
u32 ccm_cscmr1, ccm_cscdr2, sdhc_clk_sel, sdhc_clk_div;
|
||||
u32 freq = 0;
|
||||
|
||||
ccm_cscmr1 = readl(&ccm->cscmr1);
|
||||
sdhc_clk_sel = ccm_cscmr1 & CCM_CSCMR1_ESDHC1_CLK_SEL_MASK;
|
||||
sdhc_clk_sel >>= CCM_CSCMR1_ESDHC1_CLK_SEL_OFFSET;
|
||||
|
||||
ccm_cscdr2 = readl(&ccm->cscdr2);
|
||||
sdhc_clk_div = ccm_cscdr2 & CCM_CSCDR2_ESDHC1_CLK_DIV_MASK;
|
||||
sdhc_clk_div >>= CCM_CSCDR2_ESDHC1_CLK_DIV_OFFSET;
|
||||
sdhc_clk_div += 1;
|
||||
|
||||
switch (sdhc_clk_sel) {
|
||||
case 0:
|
||||
freq = PLL3_MAIN_FREQ;
|
||||
break;
|
||||
case 1:
|
||||
freq = PLL3_PFD3_FREQ;
|
||||
break;
|
||||
case 2:
|
||||
freq = PLL1_PFD3_FREQ;
|
||||
break;
|
||||
case 3:
|
||||
freq = get_bus_clk();
|
||||
break;
|
||||
}
|
||||
|
||||
return freq / sdhc_clk_div;
|
||||
}
|
||||
|
||||
u32 get_fec_clk(void)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
u32 ccm_cscmr2, rmii_clk_sel;
|
||||
u32 freq = 0;
|
||||
|
||||
ccm_cscmr2 = readl(&ccm->cscmr2);
|
||||
rmii_clk_sel = ccm_cscmr2 & CCM_CSCMR2_RMII_CLK_SEL_MASK;
|
||||
rmii_clk_sel >>= CCM_CSCMR2_RMII_CLK_SEL_OFFSET;
|
||||
|
||||
switch (rmii_clk_sel) {
|
||||
case 0:
|
||||
freq = ENET_EXTERNAL_CLK;
|
||||
break;
|
||||
case 1:
|
||||
freq = AUDIO_EXTERNAL_CLK;
|
||||
break;
|
||||
case 2:
|
||||
freq = PLL5_MAIN_FREQ;
|
||||
break;
|
||||
case 3:
|
||||
freq = PLL5_MAIN_FREQ / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static u32 get_i2c_clk(void)
|
||||
{
|
||||
return get_ipg_clk();
|
||||
}
|
||||
|
||||
static u32 get_dspi_clk(void)
|
||||
{
|
||||
return get_ipg_clk();
|
||||
}
|
||||
|
||||
u32 get_lpuart_clk(void)
|
||||
{
|
||||
return get_uart_clk();
|
||||
}
|
||||
|
||||
unsigned int mxc_get_clock(enum mxc_clock clk)
|
||||
{
|
||||
switch (clk) {
|
||||
case MXC_ARM_CLK:
|
||||
return get_mcu_main_clk();
|
||||
case MXC_BUS_CLK:
|
||||
return get_bus_clk();
|
||||
case MXC_IPG_CLK:
|
||||
return get_ipg_clk();
|
||||
case MXC_UART_CLK:
|
||||
return get_uart_clk();
|
||||
case MXC_ESDHC_CLK:
|
||||
return get_sdhc_clk();
|
||||
case MXC_FEC_CLK:
|
||||
return get_fec_clk();
|
||||
case MXC_I2C_CLK:
|
||||
return get_i2c_clk();
|
||||
case MXC_DSPI_CLK:
|
||||
return get_dspi_clk();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Dump some core clocks */
|
||||
int do_vf610_showclocks(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||
char * const argv[])
|
||||
{
|
||||
printf("\n");
|
||||
printf("cpu clock : %8d MHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000);
|
||||
printf("bus clock : %8d MHz\n", mxc_get_clock(MXC_BUS_CLK) / 1000000);
|
||||
printf("ipg clock : %8d MHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
clocks, CONFIG_SYS_MAXARGS, 1, do_vf610_showclocks,
|
||||
"display clocks",
|
||||
""
|
||||
);
|
||||
|
||||
#ifdef CONFIG_FEC_MXC
|
||||
__weak void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
|
||||
{
|
||||
struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR;
|
||||
struct fuse_bank *bank = &ocotp->bank[4];
|
||||
struct fuse_bank4_regs *fuse =
|
||||
(struct fuse_bank4_regs *)bank->fuse_regs;
|
||||
|
||||
u32 value = readl(&fuse->mac_addr0);
|
||||
mac[0] = (value >> 8);
|
||||
mac[1] = value;
|
||||
|
||||
value = readl(&fuse->mac_addr1);
|
||||
mac[2] = value >> 24;
|
||||
mac[3] = value >> 16;
|
||||
mac[4] = value >> 8;
|
||||
mac[5] = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 get_cpu_rev(void)
|
||||
{
|
||||
return MXC_CPU_VF610 << 12;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DISPLAY_CPUINFO)
|
||||
static char *get_reset_cause(void)
|
||||
{
|
||||
u32 cause;
|
||||
struct src *src_regs = (struct src *)SRC_BASE_ADDR;
|
||||
|
||||
cause = readl(&src_regs->srsr);
|
||||
writel(cause, &src_regs->srsr);
|
||||
|
||||
if (cause & SRC_SRSR_POR_RST)
|
||||
return "POWER ON RESET";
|
||||
else if (cause & SRC_SRSR_WDOG_A5)
|
||||
return "WDOG A5";
|
||||
else if (cause & SRC_SRSR_WDOG_M4)
|
||||
return "WDOG M4";
|
||||
else if (cause & SRC_SRSR_JTAG_RST)
|
||||
return "JTAG HIGH-Z";
|
||||
else if (cause & SRC_SRSR_SW_RST)
|
||||
return "SW RESET";
|
||||
else if (cause & SRC_SRSR_RESETB)
|
||||
return "EXTERNAL RESET";
|
||||
else
|
||||
return "unknown reset";
|
||||
}
|
||||
|
||||
int print_cpuinfo(void)
|
||||
{
|
||||
printf("CPU: Freescale Vybrid VF%s at %d MHz\n",
|
||||
soc_type, mxc_get_clock(MXC_ARM_CLK) / 1000000);
|
||||
printf("Reset cause: %s\n", get_reset_cause());
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int arch_cpu_init(void)
|
||||
{
|
||||
struct mscm *mscm = (struct mscm *)MSCM_BASE_ADDR;
|
||||
|
||||
soc_type[0] = mscm->cpxcount ? '6' : '5'; /*Dual Core => VF6x0 */
|
||||
soc_type[1] = mscm->cpxcfg1 ? '1' : '0'; /* L2 Cache => VFx10 */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_MISC_INIT
|
||||
int arch_misc_init(void)
|
||||
{
|
||||
char soc[6];
|
||||
|
||||
strcpy(soc, "vf");
|
||||
strcat(soc, soc_type);
|
||||
env_set("soc", soc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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_IMX
|
||||
int cpu_mmc_init(bd_t *bis)
|
||||
{
|
||||
return fsl_esdhc_mmc_init(bis);
|
||||
}
|
||||
#endif
|
||||
|
||||
int get_clocks(void)
|
||||
{
|
||||
#ifdef CONFIG_FSL_ESDHC_IMX
|
||||
gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
|
||||
void enable_caches(void)
|
||||
{
|
||||
#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
|
||||
enum dcache_option option = DCACHE_WRITETHROUGH;
|
||||
#else
|
||||
enum dcache_option option = DCACHE_WRITEBACK;
|
||||
#endif
|
||||
dcache_enable();
|
||||
icache_enable();
|
||||
|
||||
/* Enable caching on OCRAM */
|
||||
mmu_set_region_dcache_behaviour(IRAM_BASE_ADDR, IRAM_SIZE, option);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_MXC
|
||||
/* i2c_num can be from 0 - 3 */
|
||||
int enable_i2c_clk(unsigned char enable, unsigned int i2c_num)
|
||||
{
|
||||
struct ccm_reg *ccm = (struct ccm_reg *)CCM_BASE_ADDR;
|
||||
|
||||
switch (i2c_num) {
|
||||
case 0:
|
||||
clrsetbits_le32(&ccm->ccgr4, CCM_CCGR4_I2C0_CTRL_MASK,
|
||||
CCM_CCGR4_I2C0_CTRL_MASK);
|
||||
case 2:
|
||||
clrsetbits_le32(&ccm->ccgr10, CCM_CCGR10_I2C2_CTRL_MASK,
|
||||
CCM_CCGR10_I2C2_CTRL_MASK);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include <div64.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/clock.h>
|
||||
|
||||
static struct pit_reg *cur_pit = (struct pit_reg *)PIT_BASE_ADDR;
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define TIMER_LOAD_VAL 0xffffffff
|
||||
|
||||
static inline unsigned long long tick_to_time(unsigned long long tick)
|
||||
{
|
||||
tick *= CONFIG_SYS_HZ;
|
||||
do_div(tick, mxc_get_clock(MXC_IPG_CLK));
|
||||
|
||||
return tick;
|
||||
}
|
||||
|
||||
static inline unsigned long long us_to_tick(unsigned long long usec)
|
||||
{
|
||||
usec = usec * mxc_get_clock(MXC_IPG_CLK) + 999999;
|
||||
do_div(usec, 1000000);
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
__raw_writel(0, &cur_pit->mcr);
|
||||
|
||||
__raw_writel(TIMER_LOAD_VAL, &cur_pit->ldval1);
|
||||
__raw_writel(0, &cur_pit->tctrl1);
|
||||
__raw_writel(1, &cur_pit->tctrl1);
|
||||
|
||||
gd->arch.tbl = 0;
|
||||
gd->arch.tbu = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long get_ticks(void)
|
||||
{
|
||||
ulong now = TIMER_LOAD_VAL - __raw_readl(&cur_pit->cval1);
|
||||
|
||||
/* increment tbu if tbl has rolled over */
|
||||
if (now < gd->arch.tbl)
|
||||
gd->arch.tbu++;
|
||||
gd->arch.tbl = now;
|
||||
|
||||
return (((unsigned long long)gd->arch.tbu) << 32) | gd->arch.tbl;
|
||||
}
|
||||
|
||||
ulong get_timer(ulong base)
|
||||
{
|
||||
return tick_to_time(get_ticks()) - base;
|
||||
}
|
||||
|
||||
/* delay x useconds AND preserve advance timstamp value */
|
||||
void __udelay(unsigned long usec)
|
||||
{
|
||||
unsigned long long start;
|
||||
ulong tmo;
|
||||
|
||||
start = get_ticks(); /* get current timestamp */
|
||||
tmo = us_to_tick(usec); /* convert usecs to ticks */
|
||||
while ((get_ticks() - start) < tmo)
|
||||
; /* loop till time has passed */
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is derived from PowerPC code (timebase clock frequency).
|
||||
* On ARM it returns the number of timer ticks per second.
|
||||
*/
|
||||
ulong get_tbclk(void)
|
||||
{
|
||||
return mxc_get_clock(MXC_IPG_CLK);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <stdio_dev.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/global_data.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/psci.h>
|
||||
|
||||
int armv7_apply_memory_carveout(u64 *start, u64 *size)
|
||||
{
|
||||
#ifdef CONFIG_ARMV7_SECURE_RESERVE_SIZE
|
||||
if (*start + *size < CONFIG_ARMV7_SECURE_BASE ||
|
||||
*start >= (u64)CONFIG_ARMV7_SECURE_BASE +
|
||||
CONFIG_ARMV7_SECURE_RESERVE_SIZE)
|
||||
return 0;
|
||||
|
||||
/* carveout must be at the beginning or the end of the bank */
|
||||
if (*start == CONFIG_ARMV7_SECURE_BASE ||
|
||||
*start + *size == (u64)CONFIG_ARMV7_SECURE_BASE +
|
||||
CONFIG_ARMV7_SECURE_RESERVE_SIZE) {
|
||||
if (*size < CONFIG_ARMV7_SECURE_RESERVE_SIZE) {
|
||||
debug("Secure monitor larger than RAM bank!?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
*size -= CONFIG_ARMV7_SECURE_RESERVE_SIZE;
|
||||
if (*start == CONFIG_ARMV7_SECURE_BASE)
|
||||
*start += CONFIG_ARMV7_SECURE_RESERVE_SIZE;
|
||||
return 0;
|
||||
}
|
||||
debug("Secure monitor not located at beginning or end of RAM bank\n");
|
||||
return -EINVAL;
|
||||
#else /* !CONFIG_ARMV7_SECURE_RESERVE_SIZE */
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int psci_update_dt(void *fdt)
|
||||
{
|
||||
#ifdef CONFIG_ARMV7_NONSEC
|
||||
if (!armv7_boot_nonsec())
|
||||
return 0;
|
||||
#endif
|
||||
#ifndef CONFIG_ARMV7_SECURE_BASE
|
||||
/* secure code lives in RAM, keep it alive */
|
||||
fdt_add_mem_rsv(fdt, (unsigned long)__secure_start,
|
||||
__secure_end - __secure_start);
|
||||
#endif
|
||||
|
||||
return fdt_psci(fdt);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Andre Przywara, Linaro <andre.przywara@linaro.org>
|
||||
*
|
||||
* Routines to transition ARMv7 processors from secure into non-secure state
|
||||
* and from non-secure SVC into HYP mode
|
||||
* needed to enable ARMv7 virtualization for current hypervisors
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/armv7.h>
|
||||
#include <asm/gic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/secure.h>
|
||||
|
||||
static unsigned int read_id_pfr1(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
asm("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg));
|
||||
return reg;
|
||||
}
|
||||
|
||||
static unsigned long get_gicd_base_address(void)
|
||||
{
|
||||
#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
|
||||
return CONFIG_ARM_GIC_BASE_ADDRESS + GIC_DIST_OFFSET;
|
||||
#else
|
||||
unsigned periphbase;
|
||||
|
||||
/* get the GIC base address from the CBAR register */
|
||||
asm("mrc p15, 4, %0, c15, c0, 0\n" : "=r" (periphbase));
|
||||
|
||||
/* the PERIPHBASE can be mapped above 4 GB (lower 8 bits used to
|
||||
* encode this). Bail out here since we cannot access this without
|
||||
* enabling paging.
|
||||
*/
|
||||
if ((periphbase & 0xff) != 0) {
|
||||
printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (periphbase & CBAR_MASK) + GIC_DIST_OFFSET;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Define a specific version of this function to enable any available
|
||||
* hardware protections for the reserved region */
|
||||
void __weak protect_secure_section(void) {}
|
||||
|
||||
static void relocate_secure_section(void)
|
||||
{
|
||||
#ifdef CONFIG_ARMV7_SECURE_BASE
|
||||
size_t sz = __secure_end - __secure_start;
|
||||
unsigned long szflush = ALIGN(sz + 1, CONFIG_SYS_CACHELINE_SIZE);
|
||||
|
||||
memcpy((void *)CONFIG_ARMV7_SECURE_BASE, __secure_start, sz);
|
||||
|
||||
flush_dcache_range(CONFIG_ARMV7_SECURE_BASE,
|
||||
CONFIG_ARMV7_SECURE_BASE + szflush);
|
||||
protect_secure_section();
|
||||
invalidate_icache_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void kick_secondary_cpus_gic(unsigned long gicdaddr)
|
||||
{
|
||||
/* kick all CPUs (except this one) by writing to GICD_SGIR */
|
||||
writel(1U << 24, gicdaddr + GICD_SGIR);
|
||||
}
|
||||
|
||||
void __weak smp_kick_all_cpus(void)
|
||||
{
|
||||
unsigned long gic_dist_addr;
|
||||
|
||||
gic_dist_addr = get_gicd_base_address();
|
||||
if (gic_dist_addr == -1)
|
||||
return;
|
||||
|
||||
kick_secondary_cpus_gic(gic_dist_addr);
|
||||
}
|
||||
|
||||
__weak void psci_board_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
int armv7_init_nonsec(void)
|
||||
{
|
||||
unsigned int reg;
|
||||
unsigned itlinesnr, i;
|
||||
unsigned long gic_dist_addr;
|
||||
|
||||
/* check whether the CPU supports the security extensions */
|
||||
reg = read_id_pfr1();
|
||||
if ((reg & 0xF0) == 0) {
|
||||
printf("nonsec: Security extensions not implemented.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* the SCR register will be set directly in the monitor mode handler,
|
||||
* according to the spec one should not tinker with it in secure state
|
||||
* in SVC mode. Do not try to read it once in non-secure state,
|
||||
* any access to it will trap.
|
||||
*/
|
||||
|
||||
gic_dist_addr = get_gicd_base_address();
|
||||
if (gic_dist_addr == -1)
|
||||
return -1;
|
||||
|
||||
/* enable the GIC distributor */
|
||||
writel(readl(gic_dist_addr + GICD_CTLR) | 0x03,
|
||||
gic_dist_addr + GICD_CTLR);
|
||||
|
||||
/* TYPER[4:0] contains an encoded number of available interrupts */
|
||||
itlinesnr = readl(gic_dist_addr + GICD_TYPER) & 0x1f;
|
||||
|
||||
/* set all bits in the GIC group registers to one to allow access
|
||||
* from non-secure state. The first 32 interrupts are private per
|
||||
* CPU and will be set later when enabling the GIC for each core
|
||||
*/
|
||||
for (i = 1; i <= itlinesnr; i++)
|
||||
writel((unsigned)-1, gic_dist_addr + GICD_IGROUPRn + 4 * i);
|
||||
|
||||
psci_board_init();
|
||||
|
||||
/*
|
||||
* Relocate secure section before any cpu runs in secure ram.
|
||||
* smp_kick_all_cpus may enable other cores and runs into secure
|
||||
* ram, so need to relocate secure section before enabling other
|
||||
* cores.
|
||||
*/
|
||||
relocate_secure_section();
|
||||
|
||||
#ifndef CONFIG_ARMV7_PSCI
|
||||
smp_set_core_boot_addr((unsigned long)secure_ram_addr(_smp_pen), -1);
|
||||
smp_kick_all_cpus();
|
||||
#endif
|
||||
|
||||
/* call the non-sec switching code on this CPU also */
|
||||
secure_ram_addr(_nonsec_init)();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user