|
@@ -1,51 +1,18 @@
|
|
/*
|
|
/*
|
|
- * Clock tree for CSR SiRFprimaII
|
|
|
|
|
|
+ * common clks module for all SiRF SoCs
|
|
*
|
|
*
|
|
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
|
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
|
*
|
|
*
|
|
* Licensed under GPLv2 or later.
|
|
* Licensed under GPLv2 or later.
|
|
*/
|
|
*/
|
|
|
|
|
|
-#include <linux/module.h>
|
|
|
|
-#include <linux/bitops.h>
|
|
|
|
-#include <linux/io.h>
|
|
|
|
-#include <linux/clk.h>
|
|
|
|
-#include <linux/clkdev.h>
|
|
|
|
-#include <linux/clk-provider.h>
|
|
|
|
-#include <linux/of_address.h>
|
|
|
|
-#include <linux/syscore_ops.h>
|
|
|
|
-
|
|
|
|
-#define SIRFSOC_CLKC_CLK_EN0 0x0000
|
|
|
|
-#define SIRFSOC_CLKC_CLK_EN1 0x0004
|
|
|
|
-#define SIRFSOC_CLKC_REF_CFG 0x0014
|
|
|
|
-#define SIRFSOC_CLKC_CPU_CFG 0x0018
|
|
|
|
-#define SIRFSOC_CLKC_MEM_CFG 0x001c
|
|
|
|
-#define SIRFSOC_CLKC_SYS_CFG 0x0020
|
|
|
|
-#define SIRFSOC_CLKC_IO_CFG 0x0024
|
|
|
|
-#define SIRFSOC_CLKC_DSP_CFG 0x0028
|
|
|
|
-#define SIRFSOC_CLKC_GFX_CFG 0x002c
|
|
|
|
-#define SIRFSOC_CLKC_MM_CFG 0x0030
|
|
|
|
-#define SIRFSOC_CLKC_LCD_CFG 0x0034
|
|
|
|
-#define SIRFSOC_CLKC_MMC_CFG 0x0038
|
|
|
|
-#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
|
|
|
|
-#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
|
|
|
|
-#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
|
|
|
|
-#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
|
|
|
|
-#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
|
|
|
|
-#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
|
|
|
|
-#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
|
|
|
|
-#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
|
|
|
|
-#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
|
|
|
|
-#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
|
|
|
|
-#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
|
|
|
|
-#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
|
|
|
|
-#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
|
|
|
|
-
|
|
|
|
-static void *sirfsoc_clk_vbase, *sirfsoc_rsc_vbase;
|
|
|
|
-
|
|
|
|
#define KHZ 1000
|
|
#define KHZ 1000
|
|
#define MHZ (KHZ * KHZ)
|
|
#define MHZ (KHZ * KHZ)
|
|
|
|
|
|
|
|
+static void *sirfsoc_clk_vbase;
|
|
|
|
+static void *sirfsoc_rsc_vbase;
|
|
|
|
+static struct clk_onecell_data clk_data;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* SiRFprimaII clock controller
|
|
* SiRFprimaII clock controller
|
|
* - 2 oscillators: osc-26MHz, rtc-32.768KHz
|
|
* - 2 oscillators: osc-26MHz, rtc-32.768KHz
|
|
@@ -127,6 +94,7 @@ static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long *parent_rate)
|
|
unsigned long *parent_rate)
|
|
{
|
|
{
|
|
unsigned long fin, nf, nr, od;
|
|
unsigned long fin, nf, nr, od;
|
|
|
|
+ u64 dividend;
|
|
|
|
|
|
/*
|
|
/*
|
|
* fout = fin * nf / (nr * od);
|
|
* fout = fin * nf / (nr * od);
|
|
@@ -147,7 +115,10 @@ static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
nr = BIT(6);
|
|
nr = BIT(6);
|
|
od = 1;
|
|
od = 1;
|
|
|
|
|
|
- return fin * nf / (nr * od);
|
|
|
|
|
|
+ dividend = (u64)fin * nf;
|
|
|
|
+ do_div(dividend, nr * od);
|
|
|
|
+
|
|
|
|
+ return (long)dividend;
|
|
}
|
|
}
|
|
|
|
|
|
static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
@@ -186,6 +157,30 @@ static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static long cpu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
|
+ unsigned long *parent_rate)
|
|
|
|
+{
|
|
|
|
+ /*
|
|
|
|
+ * SiRF SoC has not cpu clock control,
|
|
|
|
+ * So bypass to it's parent pll.
|
|
|
|
+ */
|
|
|
|
+ struct clk *parent_clk = clk_get_parent(hw->clk);
|
|
|
|
+ struct clk *pll_parent_clk = clk_get_parent(parent_clk);
|
|
|
|
+ unsigned long pll_parent_rate = clk_get_rate(pll_parent_clk);
|
|
|
|
+ return pll_clk_round_rate(__clk_get_hw(parent_clk), rate, &pll_parent_rate);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned long cpu_clk_recalc_rate(struct clk_hw *hw,
|
|
|
|
+ unsigned long parent_rate)
|
|
|
|
+{
|
|
|
|
+ /*
|
|
|
|
+ * SiRF SoC has not cpu clock control,
|
|
|
|
+ * So return the parent pll rate.
|
|
|
|
+ */
|
|
|
|
+ struct clk *parent_clk = clk_get_parent(hw->clk);
|
|
|
|
+ return __clk_get_rate(parent_clk);
|
|
|
|
+}
|
|
|
|
+
|
|
static struct clk_ops std_pll_ops = {
|
|
static struct clk_ops std_pll_ops = {
|
|
.recalc_rate = pll_clk_recalc_rate,
|
|
.recalc_rate = pll_clk_recalc_rate,
|
|
.round_rate = pll_clk_round_rate,
|
|
.round_rate = pll_clk_round_rate,
|
|
@@ -403,6 +398,42 @@ static int dmn_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int cpu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
|
+ unsigned long parent_rate)
|
|
|
|
+{
|
|
|
|
+ int ret1, ret2;
|
|
|
|
+ struct clk *cur_parent;
|
|
|
|
+
|
|
|
|
+ if (rate == clk_get_rate(clk_pll1.hw.clk)) {
|
|
|
|
+ ret1 = clk_set_parent(hw->clk, clk_pll1.hw.clk);
|
|
|
|
+ return ret1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rate == clk_get_rate(clk_pll2.hw.clk)) {
|
|
|
|
+ ret1 = clk_set_parent(hw->clk, clk_pll2.hw.clk);
|
|
|
|
+ return ret1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rate == clk_get_rate(clk_pll3.hw.clk)) {
|
|
|
|
+ ret1 = clk_set_parent(hw->clk, clk_pll3.hw.clk);
|
|
|
|
+ return ret1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cur_parent = clk_get_parent(hw->clk);
|
|
|
|
+
|
|
|
|
+ /* switch to tmp pll before setting parent clock's rate */
|
|
|
|
+ if (cur_parent == clk_pll1.hw.clk) {
|
|
|
|
+ ret1 = clk_set_parent(hw->clk, clk_pll2.hw.clk);
|
|
|
|
+ BUG_ON(ret1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret2 = clk_set_rate(clk_pll1.hw.clk, rate);
|
|
|
|
+
|
|
|
|
+ ret1 = clk_set_parent(hw->clk, clk_pll1.hw.clk);
|
|
|
|
+
|
|
|
|
+ return ret2 ? ret2 : ret1;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct clk_ops msi_ops = {
|
|
static struct clk_ops msi_ops = {
|
|
.set_rate = dmn_clk_set_rate,
|
|
.set_rate = dmn_clk_set_rate,
|
|
.round_rate = dmn_clk_round_rate,
|
|
.round_rate = dmn_clk_round_rate,
|
|
@@ -457,6 +488,9 @@ static struct clk_dmn clk_io = {
|
|
static struct clk_ops cpu_ops = {
|
|
static struct clk_ops cpu_ops = {
|
|
.set_parent = dmn_clk_set_parent,
|
|
.set_parent = dmn_clk_set_parent,
|
|
.get_parent = dmn_clk_get_parent,
|
|
.get_parent = dmn_clk_get_parent,
|
|
|
|
+ .set_rate = cpu_clk_set_rate,
|
|
|
|
+ .round_rate = cpu_clk_round_rate,
|
|
|
|
+ .recalc_rate = cpu_clk_recalc_rate,
|
|
};
|
|
};
|
|
|
|
|
|
static struct clk_init_data clk_cpu_init = {
|
|
static struct clk_init_data clk_cpu_init = {
|
|
@@ -532,6 +566,11 @@ static struct clk_dmn clk_mm = {
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * for atlas6, gfx2d holds the bit of prima2's clk_mm
|
|
|
|
+ */
|
|
|
|
+#define clk_gfx2d clk_mm
|
|
|
|
+
|
|
static struct clk_init_data clk_lcd_init = {
|
|
static struct clk_init_data clk_lcd_init = {
|
|
.name = "lcd",
|
|
.name = "lcd",
|
|
.ops = &dmn_ops,
|
|
.ops = &dmn_ops,
|
|
@@ -569,14 +608,6 @@ static struct clk_init_data clk_mmc01_init = {
|
|
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
|
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
|
};
|
|
};
|
|
|
|
|
|
-static struct clk_dmn clk_mmc01 = {
|
|
|
|
- .regofs = SIRFSOC_CLKC_MMC_CFG,
|
|
|
|
- .enable_bit = 59,
|
|
|
|
- .hw = {
|
|
|
|
- .init = &clk_mmc01_init,
|
|
|
|
- },
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static struct clk_init_data clk_mmc23_init = {
|
|
static struct clk_init_data clk_mmc23_init = {
|
|
.name = "mmc23",
|
|
.name = "mmc23",
|
|
.ops = &dmn_ops,
|
|
.ops = &dmn_ops,
|
|
@@ -584,14 +615,6 @@ static struct clk_init_data clk_mmc23_init = {
|
|
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
|
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
|
};
|
|
};
|
|
|
|
|
|
-static struct clk_dmn clk_mmc23 = {
|
|
|
|
- .regofs = SIRFSOC_CLKC_MMC_CFG,
|
|
|
|
- .enable_bit = 60,
|
|
|
|
- .hw = {
|
|
|
|
- .init = &clk_mmc23_init,
|
|
|
|
- },
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static struct clk_init_data clk_mmc45_init = {
|
|
static struct clk_init_data clk_mmc45_init = {
|
|
.name = "mmc45",
|
|
.name = "mmc45",
|
|
.ops = &dmn_ops,
|
|
.ops = &dmn_ops,
|
|
@@ -599,14 +622,6 @@ static struct clk_init_data clk_mmc45_init = {
|
|
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
|
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
|
};
|
|
};
|
|
|
|
|
|
-static struct clk_dmn clk_mmc45 = {
|
|
|
|
- .regofs = SIRFSOC_CLKC_MMC_CFG,
|
|
|
|
- .enable_bit = 61,
|
|
|
|
- .hw = {
|
|
|
|
- .init = &clk_mmc45_init,
|
|
|
|
- },
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* peripheral controllers in io domain
|
|
* peripheral controllers in io domain
|
|
*/
|
|
*/
|
|
@@ -667,6 +682,20 @@ static struct clk_ops ios_ops = {
|
|
.disable = std_clk_disable,
|
|
.disable = std_clk_disable,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct clk_init_data clk_cphif_init = {
|
|
|
|
+ .name = "cphif",
|
|
|
|
+ .ops = &ios_ops,
|
|
|
|
+ .parent_names = std_clk_io_parents,
|
|
|
|
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct clk_std clk_cphif = {
|
|
|
|
+ .enable_bit = 20,
|
|
|
|
+ .hw = {
|
|
|
|
+ .init = &clk_cphif_init,
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
static struct clk_init_data clk_dmac0_init = {
|
|
static struct clk_init_data clk_dmac0_init = {
|
|
.name = "dmac0",
|
|
.name = "dmac0",
|
|
.ops = &ios_ops,
|
|
.ops = &ios_ops,
|
|
@@ -695,20 +724,6 @@ static struct clk_std clk_dmac1 = {
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
-static struct clk_init_data clk_nand_init = {
|
|
|
|
- .name = "nand",
|
|
|
|
- .ops = &ios_ops,
|
|
|
|
- .parent_names = std_clk_io_parents,
|
|
|
|
- .num_parents = ARRAY_SIZE(std_clk_io_parents),
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static struct clk_std clk_nand = {
|
|
|
|
- .enable_bit = 34,
|
|
|
|
- .hw = {
|
|
|
|
- .init = &clk_nand_init,
|
|
|
|
- },
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static struct clk_init_data clk_audio_init = {
|
|
static struct clk_init_data clk_audio_init = {
|
|
.name = "audio",
|
|
.name = "audio",
|
|
.ops = &ios_ops,
|
|
.ops = &ios_ops,
|
|
@@ -970,7 +985,7 @@ static const char *std_clk_sys_parents[] = {
|
|
};
|
|
};
|
|
|
|
|
|
static struct clk_init_data clk_security_init = {
|
|
static struct clk_init_data clk_security_init = {
|
|
- .name = "mf",
|
|
|
|
|
|
+ .name = "security",
|
|
.ops = &ios_ops,
|
|
.ops = &ios_ops,
|
|
.parent_names = std_clk_sys_parents,
|
|
.parent_names = std_clk_sys_parents,
|
|
.num_parents = ARRAY_SIZE(std_clk_sys_parents),
|
|
.num_parents = ARRAY_SIZE(std_clk_sys_parents),
|
|
@@ -1014,96 +1029,3 @@ static struct clk_std clk_usb1 = {
|
|
.init = &clk_usb1_init,
|
|
.init = &clk_usb1_init,
|
|
},
|
|
},
|
|
};
|
|
};
|
|
-
|
|
|
|
-enum prima2_clk_index {
|
|
|
|
- /* 0 1 2 3 4 5 6 7 8 9 */
|
|
|
|
- rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps,
|
|
|
|
- mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0,
|
|
|
|
- spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1,
|
|
|
|
- usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll,
|
|
|
|
- usb0, usb1, maxclk,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static struct clk_hw *prima2_clk_hw_array[maxclk] __initdata = {
|
|
|
|
- NULL, /* dummy */
|
|
|
|
- NULL,
|
|
|
|
- &clk_pll1.hw,
|
|
|
|
- &clk_pll2.hw,
|
|
|
|
- &clk_pll3.hw,
|
|
|
|
- &clk_mem.hw,
|
|
|
|
- &clk_sys.hw,
|
|
|
|
- &clk_security.hw,
|
|
|
|
- &clk_dsp.hw,
|
|
|
|
- &clk_gps.hw,
|
|
|
|
- &clk_mf.hw,
|
|
|
|
- &clk_io.hw,
|
|
|
|
- &clk_cpu.hw,
|
|
|
|
- &clk_uart0.hw,
|
|
|
|
- &clk_uart1.hw,
|
|
|
|
- &clk_uart2.hw,
|
|
|
|
- &clk_tsc.hw,
|
|
|
|
- &clk_i2c0.hw,
|
|
|
|
- &clk_i2c1.hw,
|
|
|
|
- &clk_spi0.hw,
|
|
|
|
- &clk_spi1.hw,
|
|
|
|
- &clk_pwmc.hw,
|
|
|
|
- &clk_efuse.hw,
|
|
|
|
- &clk_pulse.hw,
|
|
|
|
- &clk_dmac0.hw,
|
|
|
|
- &clk_dmac1.hw,
|
|
|
|
- &clk_nand.hw,
|
|
|
|
- &clk_audio.hw,
|
|
|
|
- &clk_usp0.hw,
|
|
|
|
- &clk_usp1.hw,
|
|
|
|
- &clk_usp2.hw,
|
|
|
|
- &clk_vip.hw,
|
|
|
|
- &clk_gfx.hw,
|
|
|
|
- &clk_mm.hw,
|
|
|
|
- &clk_lcd.hw,
|
|
|
|
- &clk_vpp.hw,
|
|
|
|
- &clk_mmc01.hw,
|
|
|
|
- &clk_mmc23.hw,
|
|
|
|
- &clk_mmc45.hw,
|
|
|
|
- &usb_pll_clk_hw,
|
|
|
|
- &clk_usb0.hw,
|
|
|
|
- &clk_usb1.hw,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static struct clk *prima2_clks[maxclk];
|
|
|
|
-static struct clk_onecell_data clk_data;
|
|
|
|
-
|
|
|
|
-static void __init sirfsoc_clk_init(struct device_node *np)
|
|
|
|
-{
|
|
|
|
- struct device_node *rscnp;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- rscnp = of_find_compatible_node(NULL, NULL, "sirf,prima2-rsc");
|
|
|
|
- sirfsoc_rsc_vbase = of_iomap(rscnp, 0);
|
|
|
|
- if (!sirfsoc_rsc_vbase)
|
|
|
|
- panic("unable to map rsc registers\n");
|
|
|
|
- of_node_put(rscnp);
|
|
|
|
-
|
|
|
|
- sirfsoc_clk_vbase = of_iomap(np, 0);
|
|
|
|
- if (!sirfsoc_clk_vbase)
|
|
|
|
- panic("unable to map clkc registers\n");
|
|
|
|
-
|
|
|
|
- /* These are always available (RTC and 26MHz OSC)*/
|
|
|
|
- prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL,
|
|
|
|
- CLK_IS_ROOT, 32768);
|
|
|
|
- prima2_clks[osc]= clk_register_fixed_rate(NULL, "osc", NULL,
|
|
|
|
- CLK_IS_ROOT, 26000000);
|
|
|
|
-
|
|
|
|
- for (i = pll1; i < maxclk; i++) {
|
|
|
|
- prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
|
|
|
|
- BUG_ON(IS_ERR(prima2_clks[i]));
|
|
|
|
- }
|
|
|
|
- clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
|
|
|
|
- clk_register_clkdev(prima2_clks[io], NULL, "io");
|
|
|
|
- clk_register_clkdev(prima2_clks[mem], NULL, "mem");
|
|
|
|
-
|
|
|
|
- clk_data.clks = prima2_clks;
|
|
|
|
- clk_data.clk_num = maxclk;
|
|
|
|
-
|
|
|
|
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
|
|
|
-}
|
|
|
|
-CLK_OF_DECLARE(sirfsoc_clk, "sirf,prima2-clkc", sirfsoc_clk_init);
|
|
|