/* * Samsung Exynos7420 clock driver. * Copyright (C) 2016 Samsung Electronics * Thomas Abraham * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #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_rate(struct clk *clk) { struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev); switch (clk->id) { 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_rate = exynos7420_topc_get_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 clk in_clk; 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, &in_clk); if (ret >= 0) priv->fin_freq = clk_get_rate(&in_clk); 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_rate(struct clk *clk) { struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev); struct exynos7420_clk_cmu_top0 *top0 = priv->top0; switch (clk->id) { 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_rate = exynos7420_top0_get_rate, }; static int exynos7420_clk_top0_probe(struct udevice *dev) { struct exynos7420_clk_top0_priv *priv; struct exynos7420_clk_cmu_top0 *top0; struct clk in_clk; 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, &in_clk); if (ret >= 0) { priv->mout_top0_bus0_pll_half = clk_get_rate(&in_clk); if (readl(&top0->mux_sel[1]) & (1 << 16)) priv->mout_top0_bus0_pll_half >>= 1; } return 0; } static ulong exynos7420_peric1_get_rate(struct clk *clk) { struct clk in_clk; unsigned int ret; unsigned long freq = 0; switch (clk->id) { case SCLK_UART2: ret = clk_get_by_index(clk->dev, 3, &in_clk); if (ret < 0) return ret; freq = clk_get_rate(&in_clk); break; } return freq; } static struct clk_ops exynos7420_clk_peric1_ops = { .get_rate = exynos7420_peric1_get_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, };