|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * Copyright 2011 Freescale Semiconductor, Inc.
|
|
|
+ * Copyright 2011-2013 Freescale Semiconductor, Inc.
|
|
|
* Copyright 2011 Linaro Ltd.
|
|
|
*
|
|
|
* The code contained herein is licensed under the GNU General Public
|
|
@@ -14,6 +14,7 @@
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/clkdev.h>
|
|
|
+#include <linux/delay.h>
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/of.h>
|
|
@@ -22,6 +23,12 @@
|
|
|
|
|
|
#include "clk.h"
|
|
|
#include "common.h"
|
|
|
+#include "hardware.h"
|
|
|
+
|
|
|
+#define CCR 0x0
|
|
|
+#define BM_CCR_WB_COUNT (0x7 << 16)
|
|
|
+#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
|
|
|
+#define BM_CCR_RBC_EN (0x1 << 27)
|
|
|
|
|
|
#define CCGR0 0x68
|
|
|
#define CCGR1 0x6c
|
|
@@ -67,6 +74,67 @@ void imx6q_set_chicken_bit(void)
|
|
|
writel_relaxed(val, ccm_base + CGPR);
|
|
|
}
|
|
|
|
|
|
+static void imx6q_enable_rbc(bool enable)
|
|
|
+{
|
|
|
+ u32 val;
|
|
|
+ static bool last_rbc_mode;
|
|
|
+
|
|
|
+ if (last_rbc_mode == enable)
|
|
|
+ return;
|
|
|
+ /*
|
|
|
+ * need to mask all interrupts in GPC before
|
|
|
+ * operating RBC configurations
|
|
|
+ */
|
|
|
+ imx_gpc_mask_all();
|
|
|
+
|
|
|
+ /* configure RBC enable bit */
|
|
|
+ val = readl_relaxed(ccm_base + CCR);
|
|
|
+ val &= ~BM_CCR_RBC_EN;
|
|
|
+ val |= enable ? BM_CCR_RBC_EN : 0;
|
|
|
+ writel_relaxed(val, ccm_base + CCR);
|
|
|
+
|
|
|
+ /* configure RBC count */
|
|
|
+ val = readl_relaxed(ccm_base + CCR);
|
|
|
+ val &= ~BM_CCR_RBC_BYPASS_COUNT;
|
|
|
+ val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
|
|
|
+ writel(val, ccm_base + CCR);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * need to delay at least 2 cycles of CKIL(32K)
|
|
|
+ * due to hardware design requirement, which is
|
|
|
+ * ~61us, here we use 65us for safe
|
|
|
+ */
|
|
|
+ udelay(65);
|
|
|
+
|
|
|
+ /* restore GPC interrupt mask settings */
|
|
|
+ imx_gpc_restore_all();
|
|
|
+
|
|
|
+ last_rbc_mode = enable;
|
|
|
+}
|
|
|
+
|
|
|
+static void imx6q_enable_wb(bool enable)
|
|
|
+{
|
|
|
+ u32 val;
|
|
|
+ static bool last_wb_mode;
|
|
|
+
|
|
|
+ if (last_wb_mode == enable)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* configure well bias enable bit */
|
|
|
+ val = readl_relaxed(ccm_base + CLPCR);
|
|
|
+ val &= ~BM_CLPCR_WB_PER_AT_LPM;
|
|
|
+ val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
|
|
|
+ writel_relaxed(val, ccm_base + CLPCR);
|
|
|
+
|
|
|
+ /* configure well bias count */
|
|
|
+ val = readl_relaxed(ccm_base + CCR);
|
|
|
+ val &= ~BM_CCR_WB_COUNT;
|
|
|
+ val |= enable ? BM_CCR_WB_COUNT : 0;
|
|
|
+ writel_relaxed(val, ccm_base + CCR);
|
|
|
+
|
|
|
+ last_wb_mode = enable;
|
|
|
+}
|
|
|
+
|
|
|
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
|
|
|
{
|
|
|
u32 val = readl_relaxed(ccm_base + CLPCR);
|
|
@@ -74,6 +142,8 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
|
|
|
val &= ~BM_CLPCR_LPM;
|
|
|
switch (mode) {
|
|
|
case WAIT_CLOCKED:
|
|
|
+ imx6q_enable_wb(false);
|
|
|
+ imx6q_enable_rbc(false);
|
|
|
break;
|
|
|
case WAIT_UNCLOCKED:
|
|
|
val |= 0x1 << BP_CLPCR_LPM;
|
|
@@ -92,6 +162,8 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
|
|
|
val |= 0x3 << BP_CLPCR_STBY_COUNT;
|
|
|
val |= BM_CLPCR_VSTBY;
|
|
|
val |= BM_CLPCR_SBYOS;
|
|
|
+ imx6q_enable_wb(true);
|
|
|
+ imx6q_enable_rbc(true);
|
|
|
break;
|
|
|
default:
|
|
|
return -EINVAL;
|
|
@@ -109,29 +181,29 @@ static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", };
|
|
|
static const char *periph_sels[] = { "periph_pre", "periph_clk2", };
|
|
|
static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", };
|
|
|
static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "pll3_pfd1_540m", };
|
|
|
-static const char *audio_sels[] = { "pll4_audio", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", };
|
|
|
+static const char *audio_sels[] = { "pll4_post_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", };
|
|
|
static const char *gpu_axi_sels[] = { "axi", "ahb", };
|
|
|
static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", };
|
|
|
static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", };
|
|
|
static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd9_720m", };
|
|
|
static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", };
|
|
|
-static const char *ldb_di_sels[] = { "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_pfd1_540m", };
|
|
|
-static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", };
|
|
|
+static const char *ldb_di_sels[] = { "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", };
|
|
|
+static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", };
|
|
|
static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
|
|
|
static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
|
|
|
static const char *ipu2_di0_sels[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
|
|
|
static const char *ipu2_di1_sels[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
|
|
|
static const char *hsi_tx_sels[] = { "pll3_120m", "pll2_pfd2_396m", };
|
|
|
static const char *pcie_axi_sels[] = { "axi", "ahb", };
|
|
|
-static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio", };
|
|
|
+static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_post_div", };
|
|
|
static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
|
|
|
static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", };
|
|
|
static const char *emi_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", };
|
|
|
static const char *vdo_axi_sels[] = { "axi", "ahb", };
|
|
|
static const char *vpu_axi_sels[] = { "axi", "pll2_pfd2_396m", "pll2_pfd0_352m", };
|
|
|
-static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video",
|
|
|
+static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div",
|
|
|
"dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0",
|
|
|
- "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio", };
|
|
|
+ "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_post_div", };
|
|
|
|
|
|
enum mx6q_clks {
|
|
|
dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m,
|
|
@@ -165,7 +237,7 @@ enum mx6q_clks {
|
|
|
pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg,
|
|
|
ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
|
|
|
sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
|
|
|
- usbphy2_gate, clk_max
|
|
|
+ usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, clk_max
|
|
|
};
|
|
|
|
|
|
static struct clk *clk[clk_max];
|
|
@@ -182,6 +254,21 @@ static struct clk_div_table clk_enet_ref_table[] = {
|
|
|
{ .val = 3, .div = 4, },
|
|
|
};
|
|
|
|
|
|
+static struct clk_div_table post_div_table[] = {
|
|
|
+ { .val = 2, .div = 1, },
|
|
|
+ { .val = 1, .div = 2, },
|
|
|
+ { .val = 0, .div = 4, },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+static struct clk_div_table video_div_table[] = {
|
|
|
+ { .val = 0, .div = 1, },
|
|
|
+ { .val = 1, .div = 2, },
|
|
|
+ { .val = 2, .div = 1, },
|
|
|
+ { .val = 3, .div = 4, },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
int __init mx6q_clocks_init(void)
|
|
|
{
|
|
|
struct device_node *np;
|
|
@@ -208,6 +295,14 @@ int __init mx6q_clocks_init(void)
|
|
|
base = of_iomap(np, 0);
|
|
|
WARN_ON(!base);
|
|
|
|
|
|
+ /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
|
|
|
+ if (cpu_is_imx6q() && imx6q_revision() == IMX_CHIP_REVISION_1_0) {
|
|
|
+ post_div_table[1].div = 1;
|
|
|
+ post_div_table[2].div = 1;
|
|
|
+ video_div_table[1].div = 1;
|
|
|
+ video_div_table[2].div = 1;
|
|
|
+ };
|
|
|
+
|
|
|
/* type name parent_name base div_mask */
|
|
|
clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f);
|
|
|
clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1);
|
|
@@ -260,6 +355,10 @@ int __init mx6q_clocks_init(void)
|
|
|
clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8);
|
|
|
clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2);
|
|
|
|
|
|
+ clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock);
|
|
|
+ clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock);
|
|
|
+ clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock);
|
|
|
+
|
|
|
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm");
|
|
|
base = of_iomap(np, 0);
|
|
|
WARN_ON(!base);
|
|
@@ -283,8 +382,8 @@ int __init mx6q_clocks_init(void)
|
|
|
clk[gpu3d_shader_sel] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
|
|
|
clk[ipu1_sel] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
|
|
|
clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
|
|
|
- clk[ldb_di0_sel] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
|
|
|
- clk[ldb_di1_sel] = imx_clk_mux("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
|
|
|
+ clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
|
|
|
+ clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
|
|
|
clk[ipu1_di0_pre_sel] = imx_clk_mux("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
|
|
|
clk[ipu1_di1_pre_sel] = imx_clk_mux("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
|
|
|
clk[ipu2_di0_pre_sel] = imx_clk_mux("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
|
|
@@ -332,9 +431,9 @@ int __init mx6q_clocks_init(void)
|
|
|
clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3);
|
|
|
clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3);
|
|
|
clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
|
|
|
- clk[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1);
|
|
|
+ clk[ldb_di0_podf] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0);
|
|
|
clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
|
|
|
- clk[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1);
|
|
|
+ clk[ldb_di1_podf] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0);
|
|
|
clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3);
|
|
|
clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3);
|
|
|
clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3);
|
|
@@ -443,12 +542,16 @@ int __init mx6q_clocks_init(void)
|
|
|
|
|
|
clk_register_clkdev(clk[gpt_ipg], "ipg", "imx-gpt.0");
|
|
|
clk_register_clkdev(clk[gpt_ipg_per], "per", "imx-gpt.0");
|
|
|
- clk_register_clkdev(clk[twd], NULL, "smp_twd");
|
|
|
clk_register_clkdev(clk[cko1_sel], "cko1_sel", NULL);
|
|
|
clk_register_clkdev(clk[ahb], "ahb", NULL);
|
|
|
clk_register_clkdev(clk[cko1], "cko1", NULL);
|
|
|
clk_register_clkdev(clk[arm], NULL, "cpu0");
|
|
|
|
|
|
+ if (imx6q_revision() != IMX_CHIP_REVISION_1_0) {
|
|
|
+ clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]);
|
|
|
+ clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]);
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* The gpmi needs 100MHz frequency in the EDO/Sync mode,
|
|
|
* We can not get the 100MHz from the pll2_pfd0_352m.
|