summaryrefslogtreecommitdiff
path: root/drivers/clk/tegra/clk-tegra210.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/tegra/clk-tegra210.c')
-rw-r--r--drivers/clk/tegra/clk-tegra210.c361
1 files changed, 357 insertions, 4 deletions
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 9e6260869eb9..9fb5d51ccce4 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -22,10 +22,12 @@
#include <linux/of_address.h>
#include <linux/delay.h>
#include <linux/export.h>
+#include <linux/mutex.h>
#include <linux/clk/tegra.h>
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/reset/tegra210-car.h>
#include <linux/iopoll.h>
+#include <soc/tegra/pmc.h>
#include "clk.h"
#include "clk-id.h"
@@ -41,6 +43,7 @@
#define CLK_SOURCE_CSITE 0x1d4
#define CLK_SOURCE_EMC 0x19c
#define CLK_SOURCE_SOR1 0x410
+#define CLK_SOURCE_LA 0x1f8
#define PLLC_BASE 0x80
#define PLLC_OUT 0x84
@@ -231,6 +234,30 @@
#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
+#define LVL2_CLK_GATE_OVRA 0xf8
+#define LVL2_CLK_GATE_OVRC 0x3a0
+#define LVL2_CLK_GATE_OVRD 0x3a4
+#define LVL2_CLK_GATE_OVRE 0x554
+
+/* I2S registers to handle during APE MBIST WAR */
+#define TEGRA210_I2S_BASE 0x1000
+#define TEGRA210_I2S_SIZE 0x100
+#define TEGRA210_I2S_CTRLS 5
+#define TEGRA210_I2S_CG 0x88
+#define TEGRA210_I2S_CTRL 0xa0
+
+/* DISPA registers to handle during MBIST WAR */
+#define DC_CMD_DISPLAY_COMMAND 0xc8
+#define DC_COM_DSC_TOP_CTL 0xcf8
+
+/* VIC register to handle during MBIST WAR */
+#define NV_PVIC_THI_SLCG_OVERRIDE_LOW 0x8c
+
+/* APE, DISPA and VIC base addesses needed for MBIST WAR */
+#define TEGRA210_AHUB_BASE 0x702d0000
+#define TEGRA210_DISPA_BASE 0x54200000
+#define TEGRA210_VIC_BASE 0x54340000
+
/*
* SDM fractional divisor is 16-bit 2's complement signed number within
* (-2^12 ... 2^12-1) range. Represented in PLL data structure as unsigned
@@ -255,8 +282,22 @@ static struct cpu_clk_suspend_context {
} tegra210_cpu_clk_sctx;
#endif
+struct tegra210_domain_mbist_war {
+ void (*handle_lvl2_ovr)(struct tegra210_domain_mbist_war *mbist);
+ const u32 lvl2_offset;
+ const u32 lvl2_mask;
+ const unsigned int num_clks;
+ const unsigned int *clk_init_data;
+ struct clk_bulk_data *clks;
+};
+
+static struct clk **clks;
+
static void __iomem *clk_base;
static void __iomem *pmc_base;
+static void __iomem *ahub_base;
+static void __iomem *dispa_base;
+static void __iomem *vic_base;
static unsigned long osc_freq;
static unsigned long pll_ref_freq;
@@ -267,6 +308,7 @@ static DEFINE_SPINLOCK(pll_re_lock);
static DEFINE_SPINLOCK(pll_u_lock);
static DEFINE_SPINLOCK(sor1_lock);
static DEFINE_SPINLOCK(emc_lock);
+static DEFINE_MUTEX(lvl2_ovr_lock);
/* possible OSC frequencies in Hz */
static unsigned long tegra210_input_freq[] = {
@@ -310,6 +352,8 @@ static const char *mux_pllmcp_clkm[] = {
#define PLLA_MISC2_WRITE_MASK 0x06ffffff
/* PLLD */
+#define PLLD_BASE_CSI_CLKSOURCE (1 << 23)
+
#define PLLD_MISC0_EN_SDM (1 << 16)
#define PLLD_MISC0_LOCK_OVERRIDE (1 << 17)
#define PLLD_MISC0_LOCK_ENABLE (1 << 18)
@@ -513,6 +557,115 @@ void tegra210_set_sata_pll_seq_sw(bool state)
}
EXPORT_SYMBOL_GPL(tegra210_set_sata_pll_seq_sw);
+static void tegra210_generic_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + mbist->lvl2_offset);
+ writel_relaxed(val | mbist->lvl2_mask, clk_base + mbist->lvl2_offset);
+ fence_udelay(1, clk_base);
+ writel_relaxed(val, clk_base + mbist->lvl2_offset);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_venc_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 csi_src, ovra, ovre;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pll_d_lock, flags);
+
+ csi_src = readl_relaxed(clk_base + PLLD_BASE);
+ writel_relaxed(csi_src | PLLD_BASE_CSI_CLKSOURCE, clk_base + PLLD_BASE);
+ fence_udelay(1, clk_base);
+
+ ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovra | BIT(15), clk_base + LVL2_CLK_GATE_OVRA);
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovre | BIT(3), clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(csi_src, clk_base + PLLD_BASE);
+ fence_udelay(1, clk_base);
+
+ spin_unlock_irqrestore(&pll_d_lock, flags);
+}
+
+static void tegra210_disp_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 ovra, dsc_top_ctrl;
+
+ ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovra | BIT(1), clk_base + LVL2_CLK_GATE_OVRA);
+ fence_udelay(1, clk_base);
+
+ dsc_top_ctrl = readl_relaxed(dispa_base + DC_COM_DSC_TOP_CTL);
+ writel_relaxed(dsc_top_ctrl | BIT(2), dispa_base + DC_COM_DSC_TOP_CTL);
+ readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND);
+ writel_relaxed(dsc_top_ctrl, dispa_base + DC_COM_DSC_TOP_CTL);
+ readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND);
+
+ writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_vic_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 ovre, val;
+
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovre | BIT(5), clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ val = readl_relaxed(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ writel_relaxed(val | BIT(0) | GENMASK(7, 2) | BIT(24),
+ vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ fence_udelay(1, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+
+ writel_relaxed(val, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ readl(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_ape_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ void __iomem *i2s_base;
+ unsigned int i;
+ u32 ovrc, ovre;
+
+ ovrc = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRC);
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovrc | BIT(1), clk_base + LVL2_CLK_GATE_OVRC);
+ writel_relaxed(ovre | BIT(10) | BIT(11),
+ clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ i2s_base = ahub_base + TEGRA210_I2S_BASE;
+
+ for (i = 0; i < TEGRA210_I2S_CTRLS; i++) {
+ u32 i2s_ctrl;
+
+ i2s_ctrl = readl_relaxed(i2s_base + TEGRA210_I2S_CTRL);
+ writel_relaxed(i2s_ctrl | BIT(10),
+ i2s_base + TEGRA210_I2S_CTRL);
+ writel_relaxed(0, i2s_base + TEGRA210_I2S_CG);
+ readl(i2s_base + TEGRA210_I2S_CG);
+ writel_relaxed(1, i2s_base + TEGRA210_I2S_CG);
+ writel_relaxed(i2s_ctrl, i2s_base + TEGRA210_I2S_CTRL);
+ readl(i2s_base + TEGRA210_I2S_CTRL);
+
+ i2s_base += TEGRA210_I2S_SIZE;
+ }
+
+ writel_relaxed(ovrc, clk_base + LVL2_CLK_GATE_OVRC);
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+}
+
static inline void _pll_misc_chk_default(void __iomem *base,
struct tegra_clk_pll_params *params,
u8 misc_num, u32 default_val, u32 mask)
@@ -2411,13 +2564,150 @@ static struct tegra_audio_clk_info tegra210_audio_plls[] = {
{ "pll_a1", &pll_a1_params, tegra_clk_pll_a1, "pll_ref" },
};
-static struct clk **clks;
-
static const char * const aclk_parents[] = {
"pll_a1", "pll_c", "pll_p", "pll_a_out0", "pll_c2", "pll_c3",
"clk_m"
};
+static const unsigned int nvjpg_slcg_clkids[] = { TEGRA210_CLK_NVDEC };
+static const unsigned int nvdec_slcg_clkids[] = { TEGRA210_CLK_NVJPG };
+static const unsigned int sor_slcg_clkids[] = { TEGRA210_CLK_HDA2CODEC_2X,
+ TEGRA210_CLK_HDA2HDMI, TEGRA210_CLK_DISP1, TEGRA210_CLK_DISP2 };
+static const unsigned int disp_slcg_clkids[] = { TEGRA210_CLK_LA,
+ TEGRA210_CLK_HOST1X};
+static const unsigned int xusba_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST,
+ TEGRA210_CLK_XUSB_DEV };
+static const unsigned int xusbb_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST,
+ TEGRA210_CLK_XUSB_SS };
+static const unsigned int xusbc_slcg_clkids[] = { TEGRA210_CLK_XUSB_DEV,
+ TEGRA210_CLK_XUSB_SS };
+static const unsigned int venc_slcg_clkids[] = { TEGRA210_CLK_HOST1X,
+ TEGRA210_CLK_PLL_D };
+static const unsigned int ape_slcg_clkids[] = { TEGRA210_CLK_ACLK,
+ TEGRA210_CLK_I2S0, TEGRA210_CLK_I2S1, TEGRA210_CLK_I2S2,
+ TEGRA210_CLK_I2S3, TEGRA210_CLK_I2S4, TEGRA210_CLK_SPDIF_OUT,
+ TEGRA210_CLK_D_AUDIO };
+static const unsigned int vic_slcg_clkids[] = { TEGRA210_CLK_HOST1X };
+
+static struct tegra210_domain_mbist_war tegra210_pg_mbist_war[] = {
+ [TEGRA_POWERGATE_VENC] = {
+ .handle_lvl2_ovr = tegra210_venc_mbist_war,
+ .num_clks = ARRAY_SIZE(venc_slcg_clkids),
+ .clk_init_data = venc_slcg_clkids,
+ },
+ [TEGRA_POWERGATE_SATA] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(0) | BIT(17) | BIT(19),
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
+ .lvl2_mask = BIT(2),
+ },
+ [TEGRA_POWERGATE_SOR] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .num_clks = ARRAY_SIZE(sor_slcg_clkids),
+ .clk_init_data = sor_slcg_clkids,
+ .lvl2_offset = LVL2_CLK_GATE_OVRA,
+ .lvl2_mask = BIT(1) | BIT(2),
+ },
+ [TEGRA_POWERGATE_DIS] = {
+ .handle_lvl2_ovr = tegra210_disp_mbist_war,
+ .num_clks = ARRAY_SIZE(disp_slcg_clkids),
+ .clk_init_data = disp_slcg_clkids,
+ },
+ [TEGRA_POWERGATE_DISB] = {
+ .num_clks = ARRAY_SIZE(disp_slcg_clkids),
+ .clk_init_data = disp_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRA,
+ .lvl2_mask = BIT(2),
+ },
+ [TEGRA_POWERGATE_XUSBA] = {
+ .num_clks = ARRAY_SIZE(xusba_slcg_clkids),
+ .clk_init_data = xusba_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_XUSBB] = {
+ .num_clks = ARRAY_SIZE(xusbb_slcg_clkids),
+ .clk_init_data = xusbb_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_XUSBC] = {
+ .num_clks = ARRAY_SIZE(xusbc_slcg_clkids),
+ .clk_init_data = xusbc_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_VIC] = {
+ .num_clks = ARRAY_SIZE(vic_slcg_clkids),
+ .clk_init_data = vic_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_vic_mbist_war,
+ },
+ [TEGRA_POWERGATE_NVDEC] = {
+ .num_clks = ARRAY_SIZE(nvdec_slcg_clkids),
+ .clk_init_data = nvdec_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(9) | BIT(31),
+ },
+ [TEGRA_POWERGATE_NVJPG] = {
+ .num_clks = ARRAY_SIZE(nvjpg_slcg_clkids),
+ .clk_init_data = nvjpg_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(9) | BIT(31),
+ },
+ [TEGRA_POWERGATE_AUD] = {
+ .num_clks = ARRAY_SIZE(ape_slcg_clkids),
+ .clk_init_data = ape_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_ape_mbist_war,
+ },
+ [TEGRA_POWERGATE_VE2] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRD,
+ .lvl2_mask = BIT(22),
+ },
+};
+
+int tegra210_clk_handle_mbist_war(unsigned int id)
+{
+ int err;
+ struct tegra210_domain_mbist_war *mbist_war;
+
+ if (id >= ARRAY_SIZE(tegra210_pg_mbist_war)) {
+ WARN(1, "unknown domain id in MBIST WAR handler\n");
+ return -EINVAL;
+ }
+
+ mbist_war = &tegra210_pg_mbist_war[id];
+ if (!mbist_war->handle_lvl2_ovr)
+ return 0;
+
+ if (mbist_war->num_clks && !mbist_war->clks)
+ return -ENODEV;
+
+ err = clk_bulk_prepare_enable(mbist_war->num_clks, mbist_war->clks);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&lvl2_ovr_lock);
+
+ mbist_war->handle_lvl2_ovr(mbist_war);
+
+ mutex_unlock(&lvl2_ovr_lock);
+
+ clk_bulk_disable_unprepare(mbist_war->num_clks, mbist_war->clks);
+
+ return 0;
+}
+
void tegra210_put_utmipll_in_iddq(void)
{
u32 reg;
@@ -2654,6 +2944,13 @@ static struct tegra_periph_init_data tegra210_periph[] = {
sor1_parents_idx, 0, &sor1_lock),
};
+static const char * const la_parents[] = {
+ "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_re_out1", "pll_a1", "clk_m", "pll_c4_out0"
+};
+
+static struct tegra_clk_periph tegra210_la =
+ TEGRA_CLK_PERIPH(29, 7, 9, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, 76, 0, NULL, 0);
+
static __init void tegra210_periph_clk_init(void __iomem *clk_base,
void __iomem *pmc_base)
{
@@ -2700,6 +2997,12 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
periph_clk_enb_refcnt);
clks[TEGRA210_CLK_DSIB] = clk;
+ /* la */
+ clk = tegra_clk_register_periph("la", la_parents,
+ ARRAY_SIZE(la_parents), &tegra210_la, clk_base,
+ CLK_SOURCE_LA, 0);
+ clks[TEGRA210_CLK_LA] = clk;
+
/* emc mux */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
ARRAY_SIZE(mux_pllmcp_clkm), 0,
@@ -3025,7 +3328,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA210_CLK_I2S4, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
{ TEGRA210_CLK_HOST1X, TEGRA210_CLK_PLL_P, 136000000, 1 },
{ TEGRA210_CLK_SCLK_MUX, TEGRA210_CLK_PLL_P, 0, 1 },
- { TEGRA210_CLK_SCLK, TEGRA210_CLK_CLK_MAX, 102000000, 1 },
+ { TEGRA210_CLK_SCLK, TEGRA210_CLK_CLK_MAX, 102000000, 0 },
{ TEGRA210_CLK_DFLL_SOC, TEGRA210_CLK_PLL_P, 51000000, 1 },
{ TEGRA210_CLK_DFLL_REF, TEGRA210_CLK_PLL_P, 51000000, 1 },
{ TEGRA210_CLK_SBC4, TEGRA210_CLK_PLL_P, 12000000, 1 },
@@ -3040,7 +3343,6 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA210_CLK_XUSB_DEV_SRC, TEGRA210_CLK_PLL_P_OUT_XUSB, 102000000, 0 },
{ TEGRA210_CLK_SATA, TEGRA210_CLK_PLL_P, 104000000, 0 },
{ TEGRA210_CLK_SATA_OOB, TEGRA210_CLK_PLL_P, 204000000, 0 },
- { TEGRA210_CLK_EMC, TEGRA210_CLK_CLK_MAX, 0, 1 },
{ TEGRA210_CLK_MSELECT, TEGRA210_CLK_CLK_MAX, 0, 1 },
{ TEGRA210_CLK_CSITE, TEGRA210_CLK_CLK_MAX, 0, 1 },
/* TODO find a way to enable this on-demand */
@@ -3149,6 +3451,37 @@ static int tegra210_reset_deassert(unsigned long id)
return 0;
}
+static void tegra210_mbist_clk_init(void)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(tegra210_pg_mbist_war); i++) {
+ unsigned int num_clks = tegra210_pg_mbist_war[i].num_clks;
+ struct clk_bulk_data *clk_data;
+
+ if (!num_clks)
+ continue;
+
+ clk_data = kmalloc_array(num_clks, sizeof(*clk_data),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_data))
+ return;
+
+ tegra210_pg_mbist_war[i].clks = clk_data;
+ for (j = 0; j < num_clks; j++) {
+ int clk_id = tegra210_pg_mbist_war[i].clk_init_data[j];
+ struct clk *clk = clks[clk_id];
+
+ if (WARN(IS_ERR(clk), "clk_id: %d\n", clk_id)) {
+ kfree(clk_data);
+ tegra210_pg_mbist_war[i].clks = NULL;
+ break;
+ }
+ clk_data[j].clk = clk;
+ }
+ }
+}
+
/**
* tegra210_clock_init - Tegra210-specific clock initialization
* @np: struct device_node * of the DT node for the SoC CAR IP block
@@ -3183,6 +3516,24 @@ static void __init tegra210_clock_init(struct device_node *np)
return;
}
+ ahub_base = ioremap(TEGRA210_AHUB_BASE, SZ_64K);
+ if (!ahub_base) {
+ pr_err("ioremap tegra210 APE failed\n");
+ return;
+ }
+
+ dispa_base = ioremap(TEGRA210_DISPA_BASE, SZ_256K);
+ if (!dispa_base) {
+ pr_err("ioremap tegra210 DISPA failed\n");
+ return;
+ }
+
+ vic_base = ioremap(TEGRA210_VIC_BASE, SZ_256K);
+ if (!vic_base) {
+ pr_err("ioremap tegra210 VIC failed\n");
+ return;
+ }
+
clks = tegra_clk_init(clk_base, TEGRA210_CLK_CLK_MAX,
TEGRA210_CAR_BANK_COUNT);
if (!clks)
@@ -3219,6 +3570,8 @@ static void __init tegra210_clock_init(struct device_node *np)
tegra_add_of_provider(np);
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+ tegra210_mbist_clk_init();
+
tegra_cpu_car_ops = &tegra210_cpu_car_ops;
}
CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);