clk: renesas: rcar-gen3: Factor out CPG library
R-Car V3U has a CPG different enough to not be a generic Gen3 CPG but
similar enough to reuse code. Introduce a new CPG library, factor out
the SD clock and RPC clock handling and hook them to the generic Gen3
CPG driver so we have an equal state.
Based on Linux commit [1] and [2] by Wolfram Sang
[1] 8bb67d87346a ("clk: renesas: rcar-gen3: Factor out CPG library")
[2] 6f21d145b90f ("clk: renesas: cpg-lib: Move RPC clock registration to
the library")
Signed-off-by: Hai Pham <hai.pham.ud@renesas.com>
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Marek: - Add rcar_clk_* prefix to all functions
- Rebase on changes to
clk: renesas: Introduce and use rcar_clk_get_rate64_div_table function
- Use u32_encode_bits/GENMASK bitfield ops
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index a538e7e..1686410 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -4,6 +4,9 @@
help
Enable support for clock present on Renesas RCar SoCs.
+config CLK_RCAR_CPG_LIB
+ bool "CPG/MSSR library functions"
+
config CLK_RCAR_GEN2
bool "Renesas RCar Gen2 clock driver"
def_bool y if RCAR_32
@@ -45,6 +48,7 @@
bool "Renesas RCar Gen3 clock driver"
def_bool y if RCAR_GEN3
depends on CLK_RENESAS
+ select CLK_RCAR_CPG_LIB
help
Enable this to support the clocks on Renesas RCar Gen3 SoC.
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index df6bbc2..8f82a7a 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CLK_RENESAS) += renesas-cpg-mssr.o
+obj-$(CONFIG_CLK_RCAR_CPG_LIB) += rcar-cpg-lib.o
obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o
obj-$(CONFIG_CLK_R8A774A1) += r8a774a1-cpg-mssr.o
obj-$(CONFIG_CLK_R8A774B1) += r8a774b1-cpg-mssr.o
diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c
index f8a2362..d778db6 100644
--- a/drivers/clk/renesas/clk-rcar-gen3.c
+++ b/drivers/clk/renesas/clk-rcar-gen3.c
@@ -29,34 +29,16 @@
#include "renesas-cpg-mssr.h"
#include "rcar-gen3-cpg.h"
+#include "rcar-cpg-lib.h"
#define CPG_PLL0CR 0x00d8
#define CPG_PLL2CR 0x002c
#define CPG_PLL4CR 0x01f4
-#define SDnSRCFC_SHIFT 2
-#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT)
-
-/* Non-constant mask variant of FIELD_GET/FIELD_PREP */
-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-
static const struct clk_div_table cpg_rpcsrc_div_table[] = {
{ 2, 5 }, { 3, 6 }, { 0, 0 },
};
-static const struct clk_div_table cpg_rpc_div_table[] = {
- { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 },
-};
-
-static const struct clk_div_table cpg_sdh_div_table[] = {
- { 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 },
- { STPnHCK_TABLE | 4, 16 }, { 0, 0 },
-};
-
-static const struct clk_div_table cpg_sd_div_table[] = {
- { 0, 2 }, { 1, 4 }, { 0, 0 },
-};
-
static const struct clk_div_table r8a77970_cpg_sd0h_div_table[] = {
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 },
{ 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
@@ -69,48 +51,6 @@
{ 0, 0 },
};
-static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table,
- const u32 value)
-{
- const struct clk_div_table *clkt;
-
- for (clkt = table; clkt->div; clkt++)
- if (clkt->val == value)
- return clkt->div;
- return 0;
-}
-
-static int rcar_clk_get_table_val(const struct clk_div_table *table,
- unsigned int div)
-{
- const struct clk_div_table *clkt;
-
- for (clkt = table; clkt->div; clkt++)
- if (clkt->div == div)
- return clkt->val;
- return -EINVAL;
-}
-
-static __always_inline s64
-rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate,
- void __iomem *reg, const u32 mask,
- const struct clk_div_table *table, char *name)
-{
- u32 value, div;
- u64 rate;
-
- value = field_get(mask, readl(reg));
- div = rcar_clk_get_table_div(table, value);
- if (!div)
- return -EINVAL;
-
- rate = parent_rate / div;
- debug("%s[%i] %s clk: parent=%i div=%u => rate=%llu\n",
- __func__, __LINE__, name, parent, div, rate);
-
- return rate;
-}
-
static int gen3_clk_get_parent(struct gen3_clk_priv *priv, struct clk *clk,
struct cpg_mssr_info *info, struct clk *parent)
{
@@ -156,7 +96,6 @@
const struct cpg_core_clk *core;
struct clk parent, grandparent;
int ret;
- u32 value = 0, div = 0;
/*
* The clk may be either CPG_MOD or core clock, in case this is MOD
@@ -191,45 +130,23 @@
case CLK_TYPE_GEN3_SDH:
fallthrough;
case CLK_TYPE_GEN4_SDH:
- div = DIV_ROUND_CLOSEST(gen3_clk_get_rate64(&grandparent), rate);
- value = rcar_clk_get_table_val(cpg_sdh_div_table, div);
- if (value < 0)
- return value;
-
- clrsetbits_le32(priv->base + core->offset,
- GENMASK(9, 2), value << 2);
-
- debug("%s[%i] SDH clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n",
- __func__, __LINE__, core->parent, core->offset, div, rate, value);
- break;
+ return rcar_clk_set_rate64_sdh(core->parent,
+ gen3_clk_get_rate64(&grandparent),
+ rate, priv->base + core->offset);
case CLK_TYPE_GEN3_SD:
fallthrough;
case CLK_TYPE_GEN4_SD:
- div = DIV_ROUND_CLOSEST(gen3_clk_get_rate64(&grandparent), rate);
- value = rcar_clk_get_table_val(cpg_sd_div_table, div);
- if (value < 0)
- return value;
-
- clrsetbits_le32(priv->base + core->offset,
- GENMASK(1, 0), value);
-
- debug("%s[%i] SD clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n",
- __func__, __LINE__, core->parent, core->offset, div, rate, value);
- break;
+ return rcar_clk_set_rate64_sd(core->parent,
+ gen3_clk_get_rate64(&grandparent),
+ rate, priv->base + core->offset);
case CLK_TYPE_R8A77970_SD0:
- div = gen3_clk_get_rate64(&grandparent) / rate;
- value = rcar_clk_get_table_val(cpg_sd_div_table, div);
- if (!value)
- return -EINVAL;
-
- clrsetbits_le32(priv->base + core->offset,
- GENMASK(7, 4), value << 4);
-
- debug("%s[%i] SD clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n",
- __func__, __LINE__, core->parent, core->offset, div, rate, value);
- break;
+ return rcar_clk_set_rate64_div_table(core->parent,
+ gen3_clk_get_rate64(&grandparent),
+ rate, priv->base + core->offset,
+ CPG_SDCKCR_SD0FC_MASK,
+ r8a77970_cpg_sd0_div_table, "SD");
}
return 0;
@@ -371,17 +288,9 @@
case CLK_TYPE_GEN3_SDH: /* Fixed factor 1:1 */
fallthrough;
case CLK_TYPE_GEN4_SDH: /* Fixed factor 1:1 */
- /*
- * This takes STPnHCK and STPnCK bits into consideration
- * in the table look up too, hence the inobvious GENMASK
- * below. Bits [7:5] always read zero, so this is OKish.
- */
- return rcar_clk_get_rate64_div_table(core->parent,
- gen3_clk_get_rate64(&parent),
- priv->base + core->offset,
- CPG_SDCKCR_SRCFC_MASK |
- GENMASK(9, 5),
- cpg_sdh_div_table, "SDH");
+ return rcar_clk_get_rate64_sdh(core->parent,
+ gen3_clk_get_rate64(&parent),
+ priv->base + core->offset);
case CLK_TYPE_R8A77970_SD0H:
return rcar_clk_get_rate64_div_table(core->parent,
@@ -393,11 +302,9 @@
case CLK_TYPE_GEN3_SD:
fallthrough;
case CLK_TYPE_GEN4_SD:
- return rcar_clk_get_rate64_div_table(core->parent,
- gen3_clk_get_rate64(&parent),
- priv->base + core->offset,
- CPG_SDCKCR_FC_MASK,
- cpg_sd_div_table, "SD");
+ return rcar_clk_get_rate64_sd(core->parent,
+ gen3_clk_get_rate64(&parent),
+ priv->base + core->offset);
case CLK_TYPE_R8A77970_SD0:
return rcar_clk_get_rate64_div_table(core->parent,
@@ -446,20 +353,14 @@
case CLK_TYPE_GEN3_RPC:
case CLK_TYPE_GEN4_RPC:
- return rcar_clk_get_rate64_div_table(core->parent,
- gen3_clk_get_rate64(&parent),
- priv->base + CPG_RPCCKCR,
- CPG_RPCCKCR_DIV_PRE_MASK,
- cpg_rpc_div_table, "RPC");
+ return rcar_clk_get_rate64_rpc(core->parent,
+ gen3_clk_get_rate64(&parent),
+ priv->base + CPG_RPCCKCR);
case CLK_TYPE_GEN3_RPCD2:
case CLK_TYPE_GEN4_RPCD2:
- rate = gen3_clk_get_rate64(&parent) / 2;
-
- debug("%s[%i] RPCD2 clk: parent=%i => rate=%llu\n",
- __func__, __LINE__, core->parent, rate);
-
- return rate;
+ return rcar_clk_get_rate64_rpcd2(core->parent,
+ gen3_clk_get_rate64(&parent));
}
diff --git a/drivers/clk/renesas/rcar-cpg-lib.c b/drivers/clk/renesas/rcar-cpg-lib.c
new file mode 100644
index 0000000..a2fca66
--- /dev/null
+++ b/drivers/clk/renesas/rcar-cpg-lib.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Renesas RCar Gen3 CPG MSSR driver
+ *
+ * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on the following driver from Linux kernel:
+ * r8a7796 Clock Pulse Generator / Module Standby and Software Reset
+ *
+ * Copyright (C) 2016 Glider bvba
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <wait_bit.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+
+#include <dt-bindings/clock/renesas-cpg-mssr.h>
+
+#include "renesas-cpg-mssr.h"
+#include "rcar-gen3-cpg.h"
+#include "rcar-cpg-lib.h"
+
+#define SDnSRCFC_SHIFT 2
+#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT)
+
+/* Non-constant mask variant of FIELD_GET/FIELD_PREP */
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
+static const struct clk_div_table cpg_sdh_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 },
+ { STPnHCK_TABLE | 4, 16 }, { 0, 0 },
+};
+
+static const struct clk_div_table cpg_sd_div_table[] = {
+ { 0, 2 }, { 1, 4 }, { 0, 0 },
+};
+
+static const struct clk_div_table cpg_rpc_div_table[] = {
+ { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 },
+};
+
+static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table,
+ const u32 value)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == value)
+ return clkt->div;
+ return 0;
+}
+
+static int rcar_clk_get_table_val(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return clkt->val;
+ return -EINVAL;
+}
+
+s64 rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate,
+ void __iomem *reg, const u32 mask,
+ const struct clk_div_table *table, char *name)
+{
+ u32 value, div;
+ u64 rate;
+
+ value = field_get(mask, readl(reg));
+ div = rcar_clk_get_table_div(table, value);
+ if (!div)
+ return -EINVAL;
+
+ rate = parent_rate / div;
+ debug("%s[%i] %s clk: parent=%i div=%u => rate=%llu\n",
+ __func__, __LINE__, name, parent, div, rate);
+
+ return rate;
+}
+
+int rcar_clk_set_rate64_div_table(unsigned int parent, u64 parent_rate, ulong rate,
+ void __iomem *reg, const u32 mask,
+ const struct clk_div_table *table, char *name)
+{
+ u32 value = 0, div = 0;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ value = rcar_clk_get_table_val(table, div);
+ if (value < 0)
+ return value;
+
+ clrsetbits_le32(reg, mask, field_prep(mask, value));
+
+ debug("%s[%i] %s clk: parent=%i div=%u rate=%lu => val=%u\n",
+ __func__, __LINE__, name, parent, div, rate, value);
+
+ return 0;
+}
+
+s64 rcar_clk_get_rate64_rpc(unsigned int parent, u64 parent_rate, void __iomem *reg)
+{
+ return rcar_clk_get_rate64_div_table(parent, parent_rate, reg,
+ CPG_RPCCKCR_DIV_PRE_MASK,
+ cpg_rpc_div_table, "RPC");
+}
+
+u64 rcar_clk_get_rate64_rpcd2(unsigned int parent, u64 parent_rate)
+{
+ u64 rate = 0;
+
+ rate = parent_rate / 2;
+ debug("%s[%i] RPCD2 clk: parent=%i => rate=%llu\n",
+ __func__, __LINE__, parent, rate);
+
+ return rate;
+}
+
+s64 rcar_clk_get_rate64_sdh(unsigned int parent, u64 parent_rate, void __iomem *reg)
+{
+ /*
+ * This takes STPnHCK and STPnCK bits into consideration
+ * in the table look up too, hence the inobvious GENMASK
+ * below. Bits [7:5] always read zero, so this is OKish.
+ */
+ return rcar_clk_get_rate64_div_table(parent, parent_rate, reg,
+ CPG_SDCKCR_SRCFC_MASK |
+ GENMASK(9, 5),
+ cpg_sdh_div_table, "SDH");
+}
+
+s64 rcar_clk_get_rate64_sd(unsigned int parent, u64 parent_rate, void __iomem *reg)
+{
+ return rcar_clk_get_rate64_div_table(parent, parent_rate, reg,
+ CPG_SDCKCR_FC_MASK,
+ cpg_sd_div_table, "SD");
+}
+
+int rcar_clk_set_rate64_sdh(unsigned int parent, u64 parent_rate, ulong rate,
+ void __iomem *reg)
+{
+ /*
+ * This takes STPnHCK and STPnCK bits into consideration
+ * in the table look up too, hence the inobvious GENMASK
+ * below. Bits [7:5] always read zero, so this is OKish.
+ */
+ return rcar_clk_set_rate64_div_table(parent, parent_rate, rate, reg,
+ CPG_SDCKCR_SRCFC_MASK |
+ GENMASK(9, 5),
+ cpg_sdh_div_table, "SDH");
+}
+
+int rcar_clk_set_rate64_sd(unsigned int parent, u64 parent_rate, ulong rate,
+ void __iomem *reg)
+{
+ return rcar_clk_set_rate64_div_table(parent, parent_rate, rate, reg,
+ CPG_SDCKCR_FC_MASK,
+ cpg_sd_div_table, "SD");
+}
diff --git a/drivers/clk/renesas/rcar-cpg-lib.h b/drivers/clk/renesas/rcar-cpg-lib.h
new file mode 100644
index 0000000..09b3e50
--- /dev/null
+++ b/drivers/clk/renesas/rcar-cpg-lib.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car Gen3 Clock Pulse Generator Library
+ *
+ * Copyright (C) 2015-2018 Glider bvba
+ * Copyright (C) 2019 Renesas Electronics Corp.
+ *
+ * Based on clk-rcar-gen3.c
+ *
+ * Copyright (C) 2015 Renesas Electronics Corp.
+ */
+
+#ifndef __CLK_RENESAS_RCAR_CPG_LIB_H__
+#define __CLK_RENESAS_RCAR_CPG_LIB_H__
+
+s64 rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate,
+ void __iomem *reg, const u32 mask,
+ const struct clk_div_table *table, char *name);
+
+int rcar_clk_set_rate64_div_table(unsigned int parent, u64 parent_rate, ulong rate,
+ void __iomem *reg, const u32 mask,
+ const struct clk_div_table *table, char *name);
+
+s64 rcar_clk_get_rate64_sdh(unsigned int parent, u64 parent_rate, void __iomem *reg);
+s64 rcar_clk_get_rate64_sd(unsigned int parent, u64 parent_rate, void __iomem *reg);
+s64 rcar_clk_get_rate64_rpc(unsigned int parent, u64 parent_rate, void __iomem *reg);
+u64 rcar_clk_get_rate64_rpcd2(unsigned int parent, u64 parent_rate);
+int rcar_clk_set_rate64_sdh(unsigned int parent, u64 parent_rate, ulong rate,
+ void __iomem *reg);
+int rcar_clk_set_rate64_sd(unsigned int parent, u64 parent_rate, ulong rate,
+ void __iomem *reg);
+
+#endif