summaryrefslogtreecommitdiffstats
path: root/drivers/clk/exynos/clk-exynos7420.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/exynos/clk-exynos7420.c')
-rw-r--r--drivers/clk/exynos/clk-exynos7420.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/clk/exynos/clk-exynos7420.c b/drivers/clk/exynos/clk-exynos7420.c
new file mode 100644
index 0000000000..bf5d0e6e60
--- /dev/null
+++ b/drivers/clk/exynos/clk-exynos7420.c
@@ -0,0 +1,236 @@
+/*
+ * Samsung Exynos7420 clock driver.
+ * Copyright (C) 2016 Samsung Electronics
+ * Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <clk.h>
+#include <asm/io.h>
+#include <dt-bindings/clock/exynos7420-clk.h>
+#include "clk-pll.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DIVIDER(reg, shift, mask) \
+ (((readl(reg) >> shift) & mask) + 1)
+
+/* CMU TOPC block device structure */
+struct exynos7420_clk_cmu_topc {
+ unsigned int rsvd1[68];
+ unsigned int bus0_pll_con[2];
+ unsigned int rsvd2[2];
+ unsigned int bus1_pll_con[2];
+ unsigned int rsvd3[54];
+ unsigned int mux_sel[6];
+ unsigned int rsvd4[250];
+ unsigned int div[4];
+};
+
+/* CMU TOP0 block device structure */
+struct exynos7420_clk_cmu_top0 {
+ unsigned int rsvd0[128];
+ unsigned int mux_sel[7];
+ unsigned int rsvd1[261];
+ unsigned int div_peric[5];
+};
+
+/**
+ * struct exynos7420_clk_topc_priv - private data for CMU topc clock driver.
+ *
+ * @topc: base address of the memory mapped CMU TOPC controller.
+ * @fin_freq: frequency of the Oscillator clock.
+ * @sclk_bus0_pll_a: frequency of sclk_bus0_pll_a clock.
+ * @sclk_bus1_pll_a: frequency of sclk_bus1_pll_a clock.
+ */
+struct exynos7420_clk_topc_priv {
+ struct exynos7420_clk_cmu_topc *topc;
+ unsigned long fin_freq;
+ unsigned long sclk_bus0_pll_a;
+ unsigned long sclk_bus1_pll_a;
+};
+
+/**
+ * struct exynos7420_clk_top0_priv - private data for CMU top0 clock driver.
+ *
+ * @top0: base address of the memory mapped CMU TOP0 controller.
+ * @mout_top0_bus0_pll_half: frequency of mout_top0_bus0_pll_half clock
+ * @sclk_uart2: frequency of sclk_uart2 clock.
+ */
+struct exynos7420_clk_top0_priv {
+ struct exynos7420_clk_cmu_top0 *top0;
+ unsigned long mout_top0_bus0_pll_half;
+ unsigned long sclk_uart2;
+};
+
+static ulong exynos7420_topc_get_periph_rate(struct udevice *dev, int periph)
+{
+ struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
+
+ switch (periph) {
+ case DOUT_SCLK_BUS0_PLL:
+ case SCLK_BUS0_PLL_A:
+ case SCLK_BUS0_PLL_B:
+ return priv->sclk_bus0_pll_a;
+ case DOUT_SCLK_BUS1_PLL:
+ case SCLK_BUS1_PLL_A:
+ case SCLK_BUS1_PLL_B:
+ return priv->sclk_bus1_pll_a;
+ default:
+ return 0;
+ }
+}
+
+static struct clk_ops exynos7420_clk_topc_ops = {
+ .get_periph_rate = exynos7420_topc_get_periph_rate,
+};
+
+static int exynos7420_clk_topc_probe(struct udevice *dev)
+{
+ struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
+ struct exynos7420_clk_cmu_topc *topc;
+ struct udevice *clk_dev;
+ unsigned long rate;
+ fdt_addr_t base;
+ int ret;
+
+ base = dev_get_addr(dev);
+ if (base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ topc = (struct exynos7420_clk_cmu_topc *)base;
+ priv->topc = topc;
+
+ ret = clk_get_by_index(dev, 0, &clk_dev);
+ if (ret >= 0)
+ priv->fin_freq = clk_get_rate(clk_dev);
+
+ rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq);
+ if (readl(&topc->mux_sel[1]) & (1 << 16))
+ rate >>= 1;
+ rate /= DIVIDER(&topc->div[3], 0, 0xf);
+ priv->sclk_bus0_pll_a = rate;
+
+ rate = pll145x_get_rate(&topc->bus1_pll_con[0], priv->fin_freq) /
+ DIVIDER(&topc->div[3], 8, 0xf);
+ priv->sclk_bus1_pll_a = rate;
+
+ return 0;
+}
+
+static ulong exynos7420_top0_get_periph_rate(struct udevice *dev, int periph)
+{
+ struct exynos7420_clk_top0_priv *priv = dev_get_priv(dev);
+ struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
+
+ switch (periph) {
+ case CLK_SCLK_UART2:
+ return priv->mout_top0_bus0_pll_half /
+ DIVIDER(&top0->div_peric[3], 8, 0xf);
+ default:
+ return 0;
+ }
+}
+
+static struct clk_ops exynos7420_clk_top0_ops = {
+ .get_periph_rate = exynos7420_top0_get_periph_rate,
+};
+
+static int exynos7420_clk_top0_probe(struct udevice *dev)
+{
+ struct exynos7420_clk_top0_priv *priv;
+ struct exynos7420_clk_cmu_top0 *top0;
+ struct udevice *clk_dev;
+ fdt_addr_t base;
+ int ret;
+
+ priv = dev_get_priv(dev);
+ if (!priv)
+ return -EINVAL;
+
+ base = dev_get_addr(dev);
+ if (base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ top0 = (struct exynos7420_clk_cmu_top0 *)base;
+ priv->top0 = top0;
+
+ ret = clk_get_by_index(dev, 1, &clk_dev);
+ if (ret >= 0) {
+ priv->mout_top0_bus0_pll_half =
+ clk_get_periph_rate(clk_dev, ret);
+ if (readl(&top0->mux_sel[1]) & (1 << 16))
+ priv->mout_top0_bus0_pll_half >>= 1;
+ }
+
+ return 0;
+}
+
+static ulong exynos7420_peric1_get_periph_rate(struct udevice *dev, int periph)
+{
+ struct udevice *clk_dev;
+ unsigned int ret;
+ unsigned long freq = 0;
+
+ switch (periph) {
+ case SCLK_UART2:
+ ret = clk_get_by_index(dev, 3, &clk_dev);
+ if (ret < 0)
+ return ret;
+ freq = clk_get_periph_rate(clk_dev, ret);
+ break;
+ }
+
+ return freq;
+}
+
+static struct clk_ops exynos7420_clk_peric1_ops = {
+ .get_periph_rate = exynos7420_peric1_get_periph_rate,
+};
+
+static const struct udevice_id exynos7420_clk_topc_compat[] = {
+ { .compatible = "samsung,exynos7-clock-topc" },
+ { }
+};
+
+U_BOOT_DRIVER(exynos7420_clk_topc) = {
+ .name = "exynos7420-clock-topc",
+ .id = UCLASS_CLK,
+ .of_match = exynos7420_clk_topc_compat,
+ .probe = exynos7420_clk_topc_probe,
+ .priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv),
+ .ops = &exynos7420_clk_topc_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static const struct udevice_id exynos7420_clk_top0_compat[] = {
+ { .compatible = "samsung,exynos7-clock-top0" },
+ { }
+};
+
+U_BOOT_DRIVER(exynos7420_clk_top0) = {
+ .name = "exynos7420-clock-top0",
+ .id = UCLASS_CLK,
+ .of_match = exynos7420_clk_top0_compat,
+ .probe = exynos7420_clk_top0_probe,
+ .priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv),
+ .ops = &exynos7420_clk_top0_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static const struct udevice_id exynos7420_clk_peric1_compat[] = {
+ { .compatible = "samsung,exynos7-clock-peric1" },
+ { }
+};
+
+U_BOOT_DRIVER(exynos7420_clk_peric1) = {
+ .name = "exynos7420-clock-peric1",
+ .id = UCLASS_CLK,
+ .of_match = exynos7420_clk_peric1_compat,
+ .ops = &exynos7420_clk_peric1_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
OpenPOWER on IntegriCloud