#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 },
{ 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)
{
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
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;
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,
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,
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));
}
--- /dev/null
+// 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");
+}