diff options
Diffstat (limited to 'drivers/clk/renesas/rcar-gen3-cpg.c')
| -rw-r--r-- | drivers/clk/renesas/rcar-gen3-cpg.c | 563 |
1 files changed, 365 insertions, 198 deletions
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 3dee900522b7..b954278ddd9d 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -1,285 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R-Car Gen3 Clock Pulse Generator * - * Copyright (C) 2015-2016 Glider bvba + * Copyright (C) 2015-2018 Glider bvba + * Copyright (C) 2019 Renesas Electronics Corp. * * Based on clk-rcar-gen3.c * * Copyright (C) 2015 Renesas Electronics Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. */ #include <linux/bug.h> +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/device.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/pm.h> #include <linux/slab.h> #include <linux/sys_soc.h> #include "renesas-cpg-mssr.h" +#include "rcar-cpg-lib.h" #include "rcar-gen3-cpg.h" -#define CPG_PLL0CR 0x00d8 +#define CPG_PLLECR 0x00d0 /* PLL Enable Control Register */ + +#define CPG_PLLECR_PLLST(n) BIT(8 + (n)) /* PLLn Circuit Status */ + +#define CPG_PLL0CR 0x00d8 /* PLLn Control Registers */ #define CPG_PLL2CR 0x002c #define CPG_PLL4CR 0x01f4 +#define CPG_PLLnCR_STC_MASK GENMASK(30, 24) /* PLL Circuit Mult. Ratio */ -/* - * SDn Clock - */ -#define CPG_SD_STP_HCK BIT(9) -#define CPG_SD_STP_CK BIT(8) - -#define CPG_SD_STP_MASK (CPG_SD_STP_HCK | CPG_SD_STP_CK) -#define CPG_SD_FC_MASK (0x7 << 2 | 0x3 << 0) - -#define CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) \ -{ \ - .val = ((stp_hck) ? CPG_SD_STP_HCK : 0) | \ - ((stp_ck) ? CPG_SD_STP_CK : 0) | \ - ((sd_srcfc) << 2) | \ - ((sd_fc) << 0), \ - .div = (sd_div), \ -} +#define CPG_RCKCR_CKSEL BIT(15) /* RCLK Clock Source Select */ -struct sd_div_table { - u32 val; - unsigned int div; -}; - -struct sd_clock { +/* PLL Clocks */ +struct cpg_pll_clk { struct clk_hw hw; - void __iomem *reg; - const struct sd_div_table *div_table; - unsigned int div_num; - unsigned int div_min; - unsigned int div_max; + void __iomem *pllcr_reg; + void __iomem *pllecr_reg; + unsigned int fixed_mult; + u32 pllecr_pllst_mask; }; -/* SDn divider - * sd_srcfc sd_fc div - * stp_hck stp_ck (div) (div) = sd_srcfc x sd_fc - *------------------------------------------------------------------- - * 0 0 0 (1) 1 (4) 4 - * 0 0 1 (2) 1 (4) 8 - * 1 0 2 (4) 1 (4) 16 - * 1 0 3 (8) 1 (4) 32 - * 1 0 4 (16) 1 (4) 64 - * 0 0 0 (1) 0 (2) 2 - * 0 0 1 (2) 0 (2) 4 - * 1 0 2 (4) 0 (2) 8 - * 1 0 3 (8) 0 (2) 16 - * 1 0 4 (16) 0 (2) 32 - */ -static const struct sd_div_table cpg_sd_div_table[] = { -/* CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) */ - CPG_SD_DIV_TABLE_DATA(0, 0, 0, 1, 4), - CPG_SD_DIV_TABLE_DATA(0, 0, 1, 1, 8), - CPG_SD_DIV_TABLE_DATA(1, 0, 2, 1, 16), - CPG_SD_DIV_TABLE_DATA(1, 0, 3, 1, 32), - CPG_SD_DIV_TABLE_DATA(1, 0, 4, 1, 64), - CPG_SD_DIV_TABLE_DATA(0, 0, 0, 0, 2), - CPG_SD_DIV_TABLE_DATA(0, 0, 1, 0, 4), - CPG_SD_DIV_TABLE_DATA(1, 0, 2, 0, 8), - CPG_SD_DIV_TABLE_DATA(1, 0, 3, 0, 16), - CPG_SD_DIV_TABLE_DATA(1, 0, 4, 0, 32), -}; +#define to_pll_clk(_hw) container_of(_hw, struct cpg_pll_clk, hw) -#define to_sd_clock(_hw) container_of(_hw, struct sd_clock, hw) - -static int cpg_sd_clock_enable(struct clk_hw *hw) +static unsigned long cpg_pll_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) { - struct sd_clock *clock = to_sd_clock(hw); - u32 val, sd_fc; - unsigned int i; + struct cpg_pll_clk *pll_clk = to_pll_clk(hw); + unsigned int mult; - val = readl(clock->reg); + mult = FIELD_GET(CPG_PLLnCR_STC_MASK, readl(pll_clk->pllcr_reg)) + 1; - sd_fc = val & CPG_SD_FC_MASK; - for (i = 0; i < clock->div_num; i++) - if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) - break; + return parent_rate * mult * pll_clk->fixed_mult; +} - if (i >= clock->div_num) +static int cpg_pll_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cpg_pll_clk *pll_clk = to_pll_clk(hw); + unsigned int min_mult, max_mult, mult; + unsigned long prate; + + prate = req->best_parent_rate * pll_clk->fixed_mult; + min_mult = max(div64_ul(req->min_rate, prate), 1ULL); + max_mult = min(div64_ul(req->max_rate, prate), 128ULL); + if (max_mult < min_mult) return -EINVAL; - val &= ~(CPG_SD_STP_MASK); - val |= clock->div_table[i].val & CPG_SD_STP_MASK; - - writel(val, clock->reg); + mult = DIV_ROUND_CLOSEST_ULL(req->rate, prate); + mult = clamp(mult, min_mult, max_mult); + req->rate = prate * mult; return 0; } -static void cpg_sd_clock_disable(struct clk_hw *hw) +static int cpg_pll_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { - struct sd_clock *clock = to_sd_clock(hw); + struct cpg_pll_clk *pll_clk = to_pll_clk(hw); + unsigned int mult, i; + u32 val; - writel(readl(clock->reg) | CPG_SD_STP_MASK, clock->reg); -} + mult = DIV_ROUND_CLOSEST_ULL(rate, parent_rate * pll_clk->fixed_mult); + mult = clamp(mult, 1U, 128U); -static int cpg_sd_clock_is_enabled(struct clk_hw *hw) -{ - struct sd_clock *clock = to_sd_clock(hw); + val = readl(pll_clk->pllcr_reg); + val &= ~CPG_PLLnCR_STC_MASK; + val |= FIELD_PREP(CPG_PLLnCR_STC_MASK, mult - 1); + writel(val, pll_clk->pllcr_reg); - return !(readl(clock->reg) & CPG_SD_STP_MASK); + for (i = 1000; i; i--) { + if (readl(pll_clk->pllecr_reg) & pll_clk->pllecr_pllst_mask) + return 0; + + cpu_relax(); + } + + return -ETIMEDOUT; } -static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) +static const struct clk_ops cpg_pll_clk_ops = { + .recalc_rate = cpg_pll_clk_recalc_rate, + .determine_rate = cpg_pll_clk_determine_rate, + .set_rate = cpg_pll_clk_set_rate, +}; + +static struct clk * __init cpg_pll_clk_register(const char *name, + const char *parent_name, + void __iomem *base, + unsigned int mult, + unsigned int offset, + unsigned int index) + { - struct sd_clock *clock = to_sd_clock(hw); - unsigned long rate = parent_rate; - u32 val, sd_fc; - unsigned int i; + struct cpg_pll_clk *pll_clk; + struct clk_init_data init = {}; + struct clk *clk; - val = readl(clock->reg); + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return ERR_PTR(-ENOMEM); - sd_fc = val & CPG_SD_FC_MASK; - for (i = 0; i < clock->div_num; i++) - if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) - break; + init.name = name; + init.ops = &cpg_pll_clk_ops; + init.parent_names = &parent_name; + init.num_parents = 1; - if (i >= clock->div_num) - return -EINVAL; + pll_clk->hw.init = &init; + pll_clk->pllcr_reg = base + offset; + pll_clk->pllecr_reg = base + CPG_PLLECR; + pll_clk->fixed_mult = mult; /* PLL refclk x (setting + 1) x mult */ + pll_clk->pllecr_pllst_mask = CPG_PLLECR_PLLST(index); - return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div); + clk = clk_register(NULL, &pll_clk->hw); + if (IS_ERR(clk)) + kfree(pll_clk); + + return clk; } -static unsigned int cpg_sd_clock_calc_div(struct sd_clock *clock, - unsigned long rate, - unsigned long parent_rate) -{ - unsigned int div; +/* + * Z Clock & Z2 Clock + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + * clk->rate = (parent->rate * mult / 32 ) / fixed_div + * parent - fixed parent. No clk_set_parent support + */ +#define CPG_FRQCRB 0x00000004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_FRQCRC 0x000000e0 - if (!rate) - rate = 1; +struct cpg_z_clk { + struct clk_hw hw; + void __iomem *reg; + void __iomem *kick_reg; + unsigned long max_rate; /* Maximum rate for normal mode */ + unsigned int fixed_div; + u32 mask; +}; - div = DIV_ROUND_CLOSEST(parent_rate, rate); +#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) - return clamp_t(unsigned int, div, clock->div_min, clock->div_max); -} - -static long cpg_sd_clock_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) { - struct sd_clock *clock = to_sd_clock(hw); - unsigned int div = cpg_sd_clock_calc_div(clock, rate, *parent_rate); + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult = 32 - field_get(zclk->mask, readl(zclk->reg)); - return DIV_ROUND_CLOSEST(*parent_rate, div); + return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult, + 32 * zclk->fixed_div); } -static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static int cpg_z_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - struct sd_clock *clock = to_sd_clock(hw); - unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate); - u32 val; - unsigned int i; - - for (i = 0; i < clock->div_num; i++) - if (div == clock->div_table[i].div) - break; + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int min_mult, max_mult, mult; + unsigned long rate, prate; + + rate = min(req->rate, req->max_rate); + if (rate <= zclk->max_rate) { + /* Set parent rate to initial value for normal modes */ + prate = zclk->max_rate; + } else { + /* Set increased parent rate for boost modes */ + prate = rate; + } + req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), + prate * zclk->fixed_div); - if (i >= clock->div_num) + prate = req->best_parent_rate / zclk->fixed_div; + min_mult = max(div64_ul(req->min_rate * 32ULL, prate), 1ULL); + max_mult = min(div64_ul(req->max_rate * 32ULL, prate), 32ULL); + if (max_mult < min_mult) return -EINVAL; - val = readl(clock->reg); - val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); - val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); - writel(val, clock->reg); + mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL, prate); + mult = clamp(mult, min_mult, max_mult); + req->rate = DIV_ROUND_CLOSEST_ULL((u64)prate * mult, 32); return 0; } -static const struct clk_ops cpg_sd_clock_ops = { - .enable = cpg_sd_clock_enable, - .disable = cpg_sd_clock_disable, - .is_enabled = cpg_sd_clock_is_enabled, - .recalc_rate = cpg_sd_clock_recalc_rate, - .round_rate = cpg_sd_clock_round_rate, - .set_rate = cpg_sd_clock_set_rate, +static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + unsigned int i; + + mult = DIV64_U64_ROUND_CLOSEST(rate * 32ULL * zclk->fixed_div, + parent_rate); + mult = clamp(mult, 1U, 32U); + + if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK) + return -EBUSY; + + cpg_reg_modify(zclk->reg, zclk->mask, + field_prep(zclk->mask, 32 - mult)); + + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + cpg_reg_modify(zclk->kick_reg, 0, CPG_FRQCRB_KICK); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependent on external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) + return 0; + + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .determine_rate = cpg_z_clk_determine_rate, + .set_rate = cpg_z_clk_set_rate, }; -static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, - void __iomem *base, - const char *parent_name) +static struct clk * __init __cpg_z_clk_register(const char *name, + const char *parent_name, + void __iomem *reg, + unsigned int div, + unsigned int offset, + unsigned int fcr, + unsigned int flags) { - struct clk_init_data init; - struct sd_clock *clock; + struct clk_init_data init = {}; + struct cpg_z_clk *zclk; struct clk *clk; - unsigned int i; - clock = kzalloc(sizeof(*clock), GFP_KERNEL); - if (!clock) + zclk = kzalloc(sizeof(*zclk), GFP_KERNEL); + if (!zclk) return ERR_PTR(-ENOMEM); - init.name = core->name; - init.ops = &cpg_sd_clock_ops; - init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags = flags; init.parent_names = &parent_name; init.num_parents = 1; - clock->reg = base + core->offset; - clock->hw.init = &init; - clock->div_table = cpg_sd_div_table; - clock->div_num = ARRAY_SIZE(cpg_sd_div_table); + zclk->reg = reg + fcr; + zclk->kick_reg = reg + CPG_FRQCRB; + zclk->hw.init = &init; + zclk->mask = GENMASK(offset + 4, offset); + zclk->fixed_div = div; /* PLLVCO x 1/div x SYS-CPU divider */ - clock->div_max = clock->div_table[0].div; - clock->div_min = clock->div_max; - for (i = 1; i < clock->div_num; i++) { - clock->div_max = max(clock->div_max, clock->div_table[i].div); - clock->div_min = min(clock->div_min, clock->div_table[i].div); + clk = clk_register(NULL, &zclk->hw); + if (IS_ERR(clk)) { + kfree(zclk); + return clk; } - clk = clk_register(NULL, &clock->hw); - if (IS_ERR(clk)) - kfree(clock); - + zclk->max_rate = clk_hw_get_rate(clk_hw_get_parent(&zclk->hw)) / + zclk->fixed_div; return clk; } +static struct clk * __init cpg_z_clk_register(const char *name, + const char *parent_name, + void __iomem *reg, + unsigned int div, + unsigned int offset) +{ + return __cpg_z_clk_register(name, parent_name, reg, div, offset, + CPG_FRQCRC, CLK_SET_RATE_PARENT); +} + +static struct clk * __init cpg_zg_clk_register(const char *name, + const char *parent_name, + void __iomem *reg, + unsigned int div, + unsigned int offset) +{ + return __cpg_z_clk_register(name, parent_name, reg, div, offset, + CPG_FRQCRB, 0); + +} + +static const struct clk_div_table cpg_rpcsrc_div_table[] = { + { 2, 5 }, { 3, 6 }, { 0, 0 }, +}; static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata; static unsigned int cpg_clk_extalr __initdata; static u32 cpg_mode __initdata; static u32 cpg_quirks __initdata; -#define PLL_ERRATA BIT(0) /* Missing PLL0/2/4 post-divider */ #define RCKCR_CKSEL BIT(1) /* Manual RCLK parent selection */ static const struct soc_device_attribute cpg_quirks_match[] __initconst = { { - .soc_id = "r8a7795", .revision = "ES1.0", - .data = (void *)(PLL_ERRATA | RCKCR_CKSEL), - }, - { - .soc_id = "r8a7795", .revision = "ES1.*", - .data = (void *)RCKCR_CKSEL, - }, - { .soc_id = "r8a7796", .revision = "ES1.0", - .data = (void *)RCKCR_CKSEL, + .data = (void *)(RCKCR_CKSEL), }, { /* sentinel */ } }; struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, - struct clk **clks, void __iomem *base) + struct cpg_mssr_pub *pub) { + struct raw_notifier_head *notifiers = &pub->notifiers; + void __iomem *base = pub->base0; + struct clk **clks = pub->clks; const struct clk *parent; unsigned int mult = 1; unsigned int div = 1; u32 value; - parent = clks[core->parent]; + parent = clks[core->parent & 0xffff]; /* some types use high bits */ if (IS_ERR(parent)) return ERR_CAST(parent); @@ -290,36 +361,30 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_PLL0: /* - * PLL0 is a configurable multiplier clock. Register it as a - * fixed factor clock for now as there's no generic multiplier - * clock implementation and we currently have no need to change - * the multiplier value. + * PLL0 is implemented as a custom clock, to change the + * multiplier when cpufreq changes between normal and boost + * modes. */ - value = readl(base + CPG_PLL0CR); - mult = (((value >> 24) & 0x7f) + 1) * 2; - if (cpg_quirks & PLL_ERRATA) - mult *= 2; - break; + return cpg_pll_clk_register(core->name, __clk_get_name(parent), + base, 2, CPG_PLL0CR, 0); case CLK_TYPE_GEN3_PLL1: mult = cpg_pll_config->pll1_mult; + div = cpg_pll_config->pll1_div; break; case CLK_TYPE_GEN3_PLL2: /* - * PLL2 is a configurable multiplier clock. Register it as a - * fixed factor clock for now as there's no generic multiplier - * clock implementation and we currently have no need to change - * the multiplier value. + * PLL2 is implemented as a custom clock, to change the + * multiplier when cpufreq changes between normal and boost + * modes. */ - value = readl(base + CPG_PLL2CR); - mult = (((value >> 24) & 0x7f) + 1) * 2; - if (cpg_quirks & PLL_ERRATA) - mult *= 2; - break; + return cpg_pll_clk_register(core->name, __clk_get_name(parent), + base, 2, CPG_PLL2CR, 2); case CLK_TYPE_GEN3_PLL3: mult = cpg_pll_config->pll3_mult; + div = cpg_pll_config->pll3_div; break; case CLK_TYPE_GEN3_PLL4: @@ -331,27 +396,39 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, */ value = readl(base + CPG_PLL4CR); mult = (((value >> 24) & 0x7f) + 1) * 2; - if (cpg_quirks & PLL_ERRATA) - mult *= 2; break; + case CLK_TYPE_GEN3_SDH: + return cpg_sdh_clk_register(core->name, base + core->offset, + __clk_get_name(parent), notifiers); + case CLK_TYPE_GEN3_SD: - return cpg_sd_clk_register(core, base, __clk_get_name(parent)); + return cpg_sd_clk_register(core->name, base + core->offset, + __clk_get_name(parent)); case CLK_TYPE_GEN3_R: if (cpg_quirks & RCKCR_CKSEL) { + struct cpg_simple_notifier *csn; + + csn = kzalloc(sizeof(*csn), GFP_KERNEL); + if (!csn) + return ERR_PTR(-ENOMEM); + + csn->reg = base + CPG_RCKCR; + /* * RINT is default. * Only if EXTALR is populated, we switch to it. */ - value = readl(base + CPG_RCKCR) & 0x3f; + value = readl(csn->reg) & 0x3f; if (clk_get_rate(clks[cpg_clk_extalr])) { parent = clks[cpg_clk_extalr]; - value |= BIT(15); + value |= CPG_RCKCR_CKSEL; } - writel(value, base + CPG_RCKCR); + writel(value, csn->reg); + cpg_simple_notifier_register(notifiers, csn); break; } @@ -360,6 +437,95 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, parent = clks[cpg_clk_extalr]; break; + case CLK_TYPE_GEN3_MDSEL: + /* + * Clock selectable between two parents and two fixed dividers + * using a mode pin + */ + if (cpg_mode & BIT(core->offset)) { + div = core->div & 0xffff; + } else { + parent = clks[core->parent >> 16]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + div = core->div >> 16; + } + mult = 1; + break; + + case CLK_TYPE_GEN3_Z: + return cpg_z_clk_register(core->name, __clk_get_name(parent), + base, core->div, core->offset); + + case CLK_TYPE_GEN3_ZG: + return cpg_zg_clk_register(core->name, __clk_get_name(parent), + base, core->div, core->offset); + + case CLK_TYPE_GEN3_OSC: + /* + * Clock combining OSC EXTAL predivider and a fixed divider + */ + div = cpg_pll_config->osc_prediv * core->div; + break; + + case CLK_TYPE_GEN3_RCKSEL: + /* + * Clock selectable between two parents and two fixed dividers + * using RCKCR.CKSEL + */ + if (readl(base + CPG_RCKCR) & CPG_RCKCR_CKSEL) { + div = core->div & 0xffff; + } else { + parent = clks[core->parent >> 16]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + div = core->div >> 16; + } + break; + + case CLK_TYPE_GEN3_RPCSRC: + return clk_register_divider_table(NULL, core->name, + __clk_get_name(parent), 0, + base + CPG_RPCCKCR, 3, 2, 0, + cpg_rpcsrc_div_table, + &cpg_lock); + + case CLK_TYPE_GEN3_E3_RPCSRC: + /* + * Register RPCSRC as fixed factor clock based on the + * MD[4:1] pins and CPG_RPCCKCR[4:3] register value for + * which has been set prior to booting the kernel. + */ + value = (readl(base + CPG_RPCCKCR) & GENMASK(4, 3)) >> 3; + + switch (value) { + case 0: + div = 5; + break; + case 1: + div = 3; + break; + case 2: + parent = clks[core->parent >> 16]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + div = core->div; + break; + case 3: + default: + div = 2; + break; + } + break; + + case CLK_TYPE_GEN3_RPC: + return cpg_rpc_clk_register(core->name, base + CPG_RPCCKCR, + __clk_get_name(parent), notifiers); + + case CLK_TYPE_GEN3_RPCD2: + return cpg_rpcd2_clk_register(core->name, base + CPG_RPCCKCR, + __clk_get_name(parent)); + default: return ERR_PTR(-EINVAL); } @@ -380,5 +546,6 @@ int __init rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config, if (attr) cpg_quirks = (uintptr_t)attr->data; pr_debug("%s: mode = 0x%x quirks = 0x%x\n", __func__, mode, cpg_quirks); + return 0; } |
