diff options
Diffstat (limited to 'arch/arm/mach-shmobile/clock-r8a73a4.c')
-rw-r--r-- | arch/arm/mach-shmobile/clock-r8a73a4.c | 199 |
1 files changed, 191 insertions, 8 deletions
diff --git a/arch/arm/mach-shmobile/clock-r8a73a4.c b/arch/arm/mach-shmobile/clock-r8a73a4.c index 5f7fe628b8a1..8ea5ef6c79cc 100644 --- a/arch/arm/mach-shmobile/clock-r8a73a4.c +++ b/arch/arm/mach-shmobile/clock-r8a73a4.c @@ -30,10 +30,12 @@ #define SMSTPCR2 0xe6150138 #define SMSTPCR3 0xe615013c +#define SMSTPCR4 0xe6150140 #define SMSTPCR5 0xe6150144 #define FRQCRA 0xE6150000 #define FRQCRB 0xE6150004 +#define FRQCRC 0xE61500E0 #define VCLKCR1 0xE6150008 #define VCLKCR2 0xE615000C #define VCLKCR3 0xE615001C @@ -52,6 +54,7 @@ #define HSICKCR 0xE615026C #define M4CKCR 0xE6150098 #define PLLECR 0xE61500D0 +#define PLL0CR 0xE61500D8 #define PLL1CR 0xE6150028 #define PLL2CR 0xE615002C #define PLL2SCR 0xE61501F4 @@ -177,6 +180,7 @@ static struct sh_clk_ops pll_clk_ops = { .mapping = &cpg_mapping, \ } +PLL_CLOCK(pll0_clk, &main_clk, pll_parent_main, 1, 20, PLL0CR, 0); PLL_CLOCK(pll1_clk, &main_clk, pll_parent_main, 1, 7, PLL1CR, 1); PLL_CLOCK(pll2_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2CR, 2); PLL_CLOCK(pll2s_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2SCR, 4); @@ -184,6 +188,157 @@ PLL_CLOCK(pll2h_clk, &main_div2_clk, pll_parent_main_extal, 3, 5, PLL2HCR, 5); SH_FIXED_RATIO_CLK(pll1_div2_clk, pll1_clk, div2); +static atomic_t frqcr_lock; + +/* Several clocks need to access FRQCRB, have to lock */ +static bool frqcr_kick_check(struct clk *clk) +{ + return !(ioread32(CPG_MAP(FRQCRB)) & BIT(31)); +} + +static int frqcr_kick_do(struct clk *clk) +{ + int i; + + /* set KICK bit in FRQCRB to update hardware setting, check success */ + iowrite32(ioread32(CPG_MAP(FRQCRB)) | BIT(31), CPG_MAP(FRQCRB)); + for (i = 1000; i; i--) + if (ioread32(CPG_MAP(FRQCRB)) & BIT(31)) + cpu_relax(); + else + return 0; + + return -ETIMEDOUT; +} + +static int zclk_set_rate(struct clk *clk, unsigned long rate) +{ + void __iomem *frqcrc; + int ret; + unsigned long step, p_rate; + u32 val; + + if (!clk->parent || !__clk_get(clk->parent)) + return -ENODEV; + + if (!atomic_inc_and_test(&frqcr_lock) || !frqcr_kick_check(clk)) { + ret = -EBUSY; + goto done; + } + + /* + * Users are supposed to first call clk_set_rate() only with + * clk_round_rate() results. So, we don't fix wrong rates here, but + * guard against them anyway + */ + + p_rate = clk_get_rate(clk->parent); + if (rate == p_rate) { + val = 0; + } else { + step = DIV_ROUND_CLOSEST(p_rate, 32); + + if (rate > p_rate || rate < step) { + ret = -EINVAL; + goto done; + } + + val = 32 - rate / step; + } + + frqcrc = clk->mapped_reg + (FRQCRC - (u32)clk->enable_reg); + + iowrite32((ioread32(frqcrc) & ~(clk->div_mask << clk->enable_bit)) | + (val << clk->enable_bit), frqcrc); + + ret = frqcr_kick_do(clk); + +done: + atomic_dec(&frqcr_lock); + __clk_put(clk->parent); + return ret; +} + +static long zclk_round_rate(struct clk *clk, unsigned long rate) +{ + /* + * theoretical rate = parent rate * multiplier / 32, + * where 1 <= multiplier <= 32. Therefore we should do + * multiplier = rate * 32 / parent rate + * rounded rate = parent rate * multiplier / 32. + * However, multiplication before division won't fit in 32 bits, so + * we sacrifice some precision by first dividing and then multiplying. + * To find the nearest divisor we calculate both and pick up the best + * one. This avoids 64-bit arithmetics. + */ + unsigned long step, mul_min, mul_max, rate_min, rate_max; + + rate_max = clk_get_rate(clk->parent); + + /* output freq <= parent */ + if (rate >= rate_max) + return rate_max; + + step = DIV_ROUND_CLOSEST(rate_max, 32); + /* output freq >= parent / 32 */ + if (step >= rate) + return step; + + mul_min = rate / step; + mul_max = DIV_ROUND_UP(rate, step); + rate_min = step * mul_min; + if (mul_max == mul_min) + return rate_min; + + rate_max = step * mul_max; + + if (rate_max - rate < rate - rate_min) + return rate_max; + + return rate_min; +} + +static unsigned long zclk_recalc(struct clk *clk) +{ + void __iomem *frqcrc = FRQCRC - (u32)clk->enable_reg + clk->mapped_reg; + unsigned int max = clk->div_mask + 1; + unsigned long val = ((ioread32(frqcrc) >> clk->enable_bit) & + clk->div_mask); + + return DIV_ROUND_CLOSEST(clk_get_rate(clk->parent), max) * + (max - val); +} + +static struct sh_clk_ops zclk_ops = { + .recalc = zclk_recalc, + .set_rate = zclk_set_rate, + .round_rate = zclk_round_rate, +}; + +static struct clk z_clk = { + .parent = &pll0_clk, + .div_mask = 0x1f, + .enable_bit = 8, + /* We'll need to access FRQCRB and FRQCRC */ + .enable_reg = (void __iomem *)FRQCRB, + .ops = &zclk_ops, +}; + +/* + * It seems only 1/2 divider is usable in manual mode. 1/2 / 2/3 + * switching is only available in auto-DVFS mode + */ +SH_FIXED_RATIO_CLK(pll0_div2_clk, pll0_clk, div2); + +static struct clk z2_clk = { + .parent = &pll0_div2_clk, + .div_mask = 0x1f, + .enable_bit = 0, + /* We'll need to access FRQCRB and FRQCRC */ + .enable_reg = (void __iomem *)FRQCRB, + .ops = &zclk_ops, +}; + static struct clk *main_clks[] = { &extalr_clk, &extal1_clk, @@ -195,22 +350,23 @@ static struct clk *main_clks[] = { &main_div2_clk, &fsiack_clk, &fsibck_clk, + &pll0_clk, &pll1_clk, &pll1_div2_clk, &pll2_clk, &pll2s_clk, &pll2h_clk, + &z_clk, + &pll0_div2_clk, + &z2_clk, }; /* DIV4 */ static void div4_kick(struct clk *clk) { - unsigned long value; - - /* set KICK bit in FRQCRB to update hardware setting */ - value = ioread32(CPG_MAP(FRQCRB)); - value |= (1 << 31); - iowrite32(value, CPG_MAP(FRQCRB)); + if (!WARN(!atomic_inc_and_test(&frqcr_lock), "FRQCR* lock broken!\n")) + frqcr_kick_do(clk); + atomic_dec(&frqcr_lock); } static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, 24, 0, 36, 48, 10}; @@ -349,8 +505,10 @@ static struct clk div6_clks[DIV6_NR] = { /* MSTP */ enum { MSTP217, MSTP216, MSTP207, MSTP206, MSTP204, MSTP203, - MSTP315, MSTP314, MSTP313, MSTP312, MSTP305, - MSTP522, + MSTP329, MSTP323, MSTP318, MSTP317, MSTP316, + MSTP315, MSTP314, MSTP313, MSTP312, MSTP305, MSTP300, + MSTP411, MSTP410, MSTP409, + MSTP522, MSTP515, MSTP_NR }; @@ -361,12 +519,22 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP207] = SH_CLK_MSTP32(&div6_clks[DIV6_MP], SMSTPCR2, 7, 0), /* SCIFB1 */ [MSTP216] = SH_CLK_MSTP32(&div6_clks[DIV6_MP], SMSTPCR2, 16, 0), /* SCIFB2 */ [MSTP217] = SH_CLK_MSTP32(&div6_clks[DIV6_MP], SMSTPCR2, 17, 0), /* SCIFB3 */ + [MSTP300] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 0, 0), /* IIC2 */ [MSTP305] = SH_CLK_MSTP32(&div6_clks[DIV6_MMC1],SMSTPCR3, 5, 0), /* MMCIF1 */ [MSTP312] = SH_CLK_MSTP32(&div6_clks[DIV6_SDHI2],SMSTPCR3, 12, 0), /* SDHI2 */ [MSTP313] = SH_CLK_MSTP32(&div6_clks[DIV6_SDHI1],SMSTPCR3, 13, 0), /* SDHI1 */ [MSTP314] = SH_CLK_MSTP32(&div6_clks[DIV6_SDHI0],SMSTPCR3, 14, 0), /* SDHI0 */ [MSTP315] = SH_CLK_MSTP32(&div6_clks[DIV6_MMC0],SMSTPCR3, 15, 0), /* MMCIF0 */ + [MSTP316] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 16, 0), /* IIC6 */ + [MSTP317] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 17, 0), /* IIC7 */ + [MSTP318] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 18, 0), /* IIC0 */ + [MSTP323] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 23, 0), /* IIC1 */ + [MSTP329] = SH_CLK_MSTP32(&extalr_clk, SMSTPCR3, 29, 0), /* CMT10 */ + [MSTP409] = SH_CLK_MSTP32(&main_div2_clk, SMSTPCR4, 9, 0), /* IIC5 */ + [MSTP410] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 10, 0), /* IIC4 */ + [MSTP411] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 11, 0), /* IIC3 */ [MSTP522] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR5, 22, 0), /* Thermal */ + [MSTP515] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR5, 15, 0), /* IIC8 */ }; static struct clk_lookup lookups[] = { @@ -386,6 +554,9 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("pll2s", &pll2s_clk), CLKDEV_CON_ID("pll2h", &pll2h_clk), + /* CPU clock */ + CLKDEV_DEV_ID("cpufreq-cpu0", &z_clk), + /* DIV6 */ CLKDEV_CON_ID("zb", &div6_clks[DIV6_ZB]), CLKDEV_CON_ID("vck1", &div6_clks[DIV6_VCK1]), @@ -408,6 +579,7 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP216]), CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP217]), CLKDEV_DEV_ID("rcar_thermal", &mstp_clks[MSTP522]), + CLKDEV_DEV_ID("e6520000.i2c", &mstp_clks[MSTP300]), CLKDEV_DEV_ID("sh_mmcif.1", &mstp_clks[MSTP305]), CLKDEV_DEV_ID("ee220000.mmcif", &mstp_clks[MSTP305]), CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP312]), @@ -418,6 +590,15 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("ee100000.sdhi", &mstp_clks[MSTP314]), CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP315]), CLKDEV_DEV_ID("ee200000.mmcif", &mstp_clks[MSTP315]), + CLKDEV_DEV_ID("e6550000.i2c", &mstp_clks[MSTP316]), + CLKDEV_DEV_ID("e6560000.i2c", &mstp_clks[MSTP317]), + CLKDEV_DEV_ID("e6500000.i2c", &mstp_clks[MSTP318]), + CLKDEV_DEV_ID("e6510000.i2c", &mstp_clks[MSTP323]), + CLKDEV_DEV_ID("sh_cmt.10", &mstp_clks[MSTP329]), + CLKDEV_DEV_ID("e60b0000.i2c", &mstp_clks[MSTP409]), + CLKDEV_DEV_ID("e6540000.i2c", &mstp_clks[MSTP410]), + CLKDEV_DEV_ID("e6530000.i2c", &mstp_clks[MSTP411]), + CLKDEV_DEV_ID("e6570000.i2c", &mstp_clks[MSTP515]), /* for DT */ CLKDEV_DEV_ID("e61f0000.thermal", &mstp_clks[MSTP522]), @@ -429,6 +610,8 @@ void __init r8a73a4_clock_init(void) int k, ret = 0; u32 ckscr; + atomic_set(&frqcr_lock, -1); + reg = ioremap_nocache(CKSCR, PAGE_SIZE); BUG_ON(!reg); ckscr = ioread32(reg); |