summaryrefslogtreecommitdiff
path: root/drivers/clk/renesas/rcar-gen4-cpg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/renesas/rcar-gen4-cpg.c')
-rw-r--r--drivers/clk/renesas/rcar-gen4-cpg.c306
1 files changed, 271 insertions, 35 deletions
diff --git a/drivers/clk/renesas/rcar-gen4-cpg.c b/drivers/clk/renesas/rcar-gen4-cpg.c
index e27832e5114f..ac2b5afec46d 100644
--- a/drivers/clk/renesas/rcar-gen4-cpg.c
+++ b/drivers/clk/renesas/rcar-gen4-cpg.c
@@ -17,6 +17,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/slab.h>
#include "renesas-cpg-mssr.h"
@@ -27,12 +28,241 @@ static const struct rcar_gen4_cpg_pll_config *cpg_pll_config __initdata;
static unsigned int cpg_clk_extalr __initdata;
static u32 cpg_mode __initdata;
+#define CPG_PLLECR 0x0820 /* PLL Enable Control Register */
+
+#define CPG_PLLECR_PLLST(n) BIT(8 + ((n) < 3 ? (n) - 1 : \
+ (n) > 3 ? (n) + 1 : n)) /* PLLn Circuit Status */
+
+#define CPG_PLL1CR0 0x830 /* PLLn Control Registers */
+#define CPG_PLL1CR1 0x8b0
+#define CPG_PLL2CR0 0x834
+#define CPG_PLL2CR1 0x8b8
+#define CPG_PLL3CR0 0x83c
+#define CPG_PLL3CR1 0x8c0
+#define CPG_PLL4CR0 0x844
+#define CPG_PLL4CR1 0x8c8
+#define CPG_PLL6CR0 0x84c
+#define CPG_PLL6CR1 0x8d8
+
+#define CPG_PLLxCR0_KICK BIT(31)
+#define CPG_PLLxCR0_SSMODE GENMASK(18, 16) /* PLL mode */
+#define CPG_PLLxCR0_SSMODE_FM BIT(18) /* Fractional Multiplication */
+#define CPG_PLLxCR0_SSMODE_DITH BIT(17) /* Frequency Dithering */
+#define CPG_PLLxCR0_SSMODE_CENT BIT(16) /* Center (vs. Down) Spread Dithering */
+#define CPG_PLLxCR0_SSFREQ GENMASK(14, 8) /* SSCG Modulation Frequency */
+#define CPG_PLLxCR0_SSDEPT GENMASK(6, 0) /* SSCG Modulation Depth */
+
+/* Fractional 8.25 PLL */
+#define CPG_PLLxCR0_NI8 GENMASK(27, 20) /* Integer mult. factor */
+#define CPG_PLLxCR1_NF25 GENMASK(24, 0) /* Fractional mult. factor */
+
+/* Fractional 9.24 PLL */
+#define CPG_PLLxCR0_NI9 GENMASK(28, 20) /* Integer mult. factor */
+#define CPG_PLLxCR1_NF24 GENMASK(23, 0) /* Fractional mult. factor */
+
+#define CPG_PLLxCR_STC GENMASK(30, 24) /* R_Car V3U PLLxCR */
+
+#define CPG_RPCCKCR 0x874 /* RPC Clock Freq. Control Register */
+
+#define CPG_SD0CKCR1 0x8a4 /* SD-IF0 Clock Freq. Control Reg. 1 */
+
+#define CPG_SD0CKCR1_SDSRC_SEL GENMASK(30, 29) /* SDSRC clock freq. select */
+
+/* PLL Clocks */
+struct cpg_pll_clk {
+ struct clk_hw hw;
+ void __iomem *pllcr0_reg;
+ void __iomem *pllcr1_reg;
+ void __iomem *pllecr_reg;
+ u32 pllecr_pllst_mask;
+};
+
+#define to_pll_clk(_hw) container_of(_hw, struct cpg_pll_clk, hw)
+
+static unsigned long cpg_pll_8_25_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
+ u32 cr0 = readl(pll_clk->pllcr0_reg);
+ unsigned int ni, nf;
+ unsigned long rate;
+
+ ni = (FIELD_GET(CPG_PLLxCR0_NI8, cr0) + 1) * 2;
+ rate = parent_rate * ni;
+ if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
+ nf = FIELD_GET(CPG_PLLxCR1_NF25, readl(pll_clk->pllcr1_reg));
+ rate += mul_u64_u32_shr(parent_rate, nf, 24);
+ }
+
+ return rate;
+}
+
+static int cpg_pll_8_25_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, ni, nf;
+ u32 cr0 = readl(pll_clk->pllcr0_reg);
+ unsigned long prate;
+
+ prate = req->best_parent_rate * 2;
+ min_mult = max(div64_ul(req->min_rate, prate), 1ULL);
+ max_mult = min(div64_ul(req->max_rate, prate), 256ULL);
+ if (max_mult < min_mult)
+ return -EINVAL;
+
+ if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
+ ni = div64_ul(req->rate, prate);
+ if (ni < min_mult) {
+ ni = min_mult;
+ nf = 0;
+ } else {
+ ni = min(ni, max_mult);
+ nf = div64_ul((u64)(req->rate - prate * ni) << 24,
+ req->best_parent_rate);
+ }
+ } else {
+ ni = DIV_ROUND_CLOSEST_ULL(req->rate, prate);
+ ni = clamp(ni, min_mult, max_mult);
+ nf = 0;
+ }
+ req->rate = prate * ni + mul_u64_u32_shr(req->best_parent_rate, nf, 24);
+
+ return 0;
+}
+
+static int cpg_pll_8_25_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
+ unsigned long prate = parent_rate * 2;
+ u32 cr0 = readl(pll_clk->pllcr0_reg);
+ unsigned int ni, nf;
+ u32 val;
+
+ if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
+ ni = div64_ul(rate, prate);
+ if (ni < 1) {
+ ni = 1;
+ nf = 0;
+ } else {
+ ni = min(ni, 256U);
+ nf = div64_ul((u64)(rate - prate * ni) << 24,
+ parent_rate);
+ }
+ } else {
+ ni = DIV_ROUND_CLOSEST_ULL(rate, prate);
+ ni = clamp(ni, 1U, 256U);
+ }
+
+ if (readl(pll_clk->pllcr0_reg) & CPG_PLLxCR0_KICK)
+ return -EBUSY;
+
+ cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_NI8,
+ FIELD_PREP(CPG_PLLxCR0_NI8, ni - 1));
+ if (cr0 & CPG_PLLxCR0_SSMODE_FM)
+ cpg_reg_modify(pll_clk->pllcr1_reg, CPG_PLLxCR1_NF25,
+ FIELD_PREP(CPG_PLLxCR1_NF25, nf));
+
+ /*
+ * Set KICK bit in PLLxCR0 to update hardware setting and wait for
+ * clock change completion.
+ */
+ cpg_reg_modify(pll_clk->pllcr0_reg, 0, CPG_PLLxCR0_KICK);
+
+ /*
+ * Note: There is no HW information about the worst case latency.
+ *
+ * Using experimental measurements, it seems that no more than
+ * ~45 µs are needed, independently of the CPU rate.
+ * Since this value might be dependent on external xtal rate, pll
+ * rate or even the other emulation clocks rate, use 1000 as a
+ * "super" safe value.
+ */
+ return readl_poll_timeout(pll_clk->pllecr_reg, val,
+ val & pll_clk->pllecr_pllst_mask, 0, 1000);
+}
+
+static const struct clk_ops cpg_pll_f8_25_clk_ops = {
+ .recalc_rate = cpg_pll_8_25_clk_recalc_rate,
+};
+
+static const struct clk_ops cpg_pll_v8_25_clk_ops = {
+ .recalc_rate = cpg_pll_8_25_clk_recalc_rate,
+ .determine_rate = cpg_pll_8_25_clk_determine_rate,
+ .set_rate = cpg_pll_8_25_clk_set_rate,
+};
+
+static unsigned long cpg_pll_9_24_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
+ u32 cr0 = readl(pll_clk->pllcr0_reg);
+ unsigned int ni, nf;
+ unsigned long rate;
+
+ ni = FIELD_GET(CPG_PLLxCR0_NI9, cr0) + 1;
+ rate = parent_rate * ni;
+ if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
+ nf = FIELD_GET(CPG_PLLxCR1_NF24, readl(pll_clk->pllcr1_reg));
+ rate += mul_u64_u32_shr(parent_rate, nf, 24);
+ } else {
+ rate *= 2;
+ }
+
+ return rate;
+}
+
+static const struct clk_ops cpg_pll_f9_24_clk_ops = {
+ .recalc_rate = cpg_pll_9_24_clk_recalc_rate,
+};
+
+static struct clk * __init cpg_pll_clk_register(const char *name,
+ const char *parent_name,
+ void __iomem *base,
+ unsigned int index,
+ const struct clk_ops *ops)
+{
+ static const struct { u16 cr0, cr1; } pll_cr_offsets[] __initconst = {
+ [1 - 1] = { CPG_PLL1CR0, CPG_PLL1CR1 },
+ [2 - 1] = { CPG_PLL2CR0, CPG_PLL2CR1 },
+ [3 - 1] = { CPG_PLL3CR0, CPG_PLL3CR1 },
+ [4 - 1] = { CPG_PLL4CR0, CPG_PLL4CR1 },
+ [6 - 1] = { CPG_PLL6CR0, CPG_PLL6CR1 },
+ };
+ struct clk_init_data init = {};
+ struct cpg_pll_clk *pll_clk;
+ struct clk *clk;
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (!pll_clk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll_clk->hw.init = &init;
+ pll_clk->pllcr0_reg = base + pll_cr_offsets[index - 1].cr0;
+ pll_clk->pllcr1_reg = base + pll_cr_offsets[index - 1].cr1;
+ pll_clk->pllecr_reg = base + CPG_PLLECR;
+ pll_clk->pllecr_pllst_mask = CPG_PLLECR_PLLST(index);
+
+ clk = clk_register(NULL, &pll_clk->hw);
+ if (IS_ERR(clk))
+ kfree(pll_clk);
+
+ return clk;
+}
+
/*
- * Z0 Clock & Z1 Clock
+ * Z0, Z1 and ZG Clock
*/
#define CPG_FRQCRB 0x00000804
#define CPG_FRQCRB_KICK BIT(31)
-#define CPG_FRQCRC 0x00000808
+#define CPG_FRQCRC0 0x00000808
+#define CPG_FRQCRC1 0x000008e0
struct cpg_z_clk {
struct clk_hw hw;
@@ -49,11 +279,7 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct cpg_z_clk *zclk = to_z_clk(hw);
- unsigned int mult;
- u32 val;
-
- val = readl(zclk->reg) & zclk->mask;
- mult = 32 - (val >> __ffs(zclk->mask));
+ unsigned int mult = 32 - field_get(zclk->mask, readl(zclk->reg));
return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult,
32 * zclk->fixed_div);
@@ -104,7 +330,8 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
return -EBUSY;
- cpg_reg_modify(zclk->reg, zclk->mask, (32 - mult) << __ffs(zclk->mask));
+ 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
@@ -157,7 +384,17 @@ static struct clk * __init cpg_z_clk_register(const char *name,
init.parent_names = &parent_name;
init.num_parents = 1;
- zclk->reg = reg + CPG_FRQCRC;
+ if (offset < 32) {
+ zclk->reg = reg + CPG_FRQCRC0;
+ } else if (offset < 64) {
+ zclk->reg = reg + CPG_FRQCRC1;
+ offset -= 32;
+ } else if (offset < 96) {
+ zclk->reg = reg + CPG_FRQCRB;
+ offset -= 64;
+ } else {
+ return ERR_PTR(-EINVAL);
+ }
zclk->kick_reg = reg + CPG_FRQCRB;
zclk->hw.init = &init;
zclk->mask = GENMASK(offset + 4, offset);
@@ -183,9 +420,11 @@ static const struct clk_div_table cpg_rpcsrc_div_table[] = {
struct clk * __init rcar_gen4_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 raw_notifier_head *notifiers)
+ 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;
@@ -205,42 +444,41 @@ struct clk * __init rcar_gen4_cpg_clk_register(struct device *dev,
div = cpg_pll_config->pll1_div;
break;
- case CLK_TYPE_GEN4_PLL2:
- mult = cpg_pll_config->pll2_mult;
- div = cpg_pll_config->pll2_div;
- break;
-
- case CLK_TYPE_GEN4_PLL3:
- mult = cpg_pll_config->pll3_mult;
- div = cpg_pll_config->pll3_div;
- break;
-
- case CLK_TYPE_GEN4_PLL4:
- mult = cpg_pll_config->pll4_mult;
- div = cpg_pll_config->pll4_div;
- break;
-
case CLK_TYPE_GEN4_PLL5:
mult = cpg_pll_config->pll5_mult;
div = cpg_pll_config->pll5_div;
break;
- case CLK_TYPE_GEN4_PLL6:
- mult = cpg_pll_config->pll6_mult;
- div = cpg_pll_config->pll6_div;
- break;
-
case CLK_TYPE_GEN4_PLL2X_3X:
value = readl(base + core->offset);
- mult = (((value >> 24) & 0x7f) + 1) * 2;
+ mult = (FIELD_GET(CPG_PLLxCR_STC, value) + 1) * 2;
break;
+ case CLK_TYPE_GEN4_PLL_F8_25:
+ return cpg_pll_clk_register(core->name, __clk_get_name(parent),
+ base, core->offset,
+ &cpg_pll_f8_25_clk_ops);
+
+ case CLK_TYPE_GEN4_PLL_V8_25:
+ return cpg_pll_clk_register(core->name, __clk_get_name(parent),
+ base, core->offset,
+ &cpg_pll_v8_25_clk_ops);
+
+ case CLK_TYPE_GEN4_PLL_V9_24:
+ /* Variable fractional 9.24 is not yet supported, using fixed */
+ fallthrough;
+ case CLK_TYPE_GEN4_PLL_F9_24:
+ return cpg_pll_clk_register(core->name, __clk_get_name(parent),
+ base, core->offset,
+ &cpg_pll_f9_24_clk_ops);
+
case CLK_TYPE_GEN4_Z:
return cpg_z_clk_register(core->name, __clk_get_name(parent),
base, core->div, core->offset);
case CLK_TYPE_GEN4_SDSRC:
- div = ((readl(base + SD0CKCR1) >> 29) & 0x03) + 4;
+ value = readl(base + CPG_SD0CKCR1);
+ div = FIELD_GET(CPG_SD0CKCR1_SDSRC_SEL, value) + 4;
break;
case CLK_TYPE_GEN4_SDH:
@@ -304,7 +542,5 @@ int __init rcar_gen4_cpg_init(const struct rcar_gen4_cpg_pll_config *config,
cpg_clk_extalr = clk_extalr;
cpg_mode = mode;
- spin_lock_init(&cpg_lock);
-
return 0;
}