]> Pileus Git - ~andy/linux/blobdiff - arch/arm/mach-imx/clk-imx6q.c
Merge git://git.infradead.org/users/willy/linux-nvme
[~andy/linux] / arch / arm / mach-imx / clk-imx6q.c
index d38e54f5b6d7f1dbbbd245a91db8be9a2408b28f..151259003086e8d1f0db34015742ab307b3f986a 100644 (file)
@@ -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>
 
 #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_usb_otg", };
-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 *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);
@@ -448,6 +547,11 @@ int __init mx6q_clocks_init(void)
        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.