diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Kconfig | 1 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/exynos/Kconfig | 18 | ||||
-rw-r--r-- | drivers/clk/exynos/Makefile | 9 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-exynos7420.c | 236 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-pll.c | 33 | ||||
-rw-r--r-- | drivers/clk/exynos/clk-pll.h | 9 |
7 files changed, 307 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a98b74b..6eee8eb 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -21,5 +21,6 @@ config SPL_CLK used as U-Boot proper. source "drivers/clk/uniphier/Kconfig" +source "drivers/clk/exynos/Kconfig" endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c51db15..81fe600 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ +obj-$(CONFIG_CLK_EXYNOS) += exynos/ diff --git a/drivers/clk/exynos/Kconfig b/drivers/clk/exynos/Kconfig new file mode 100644 index 0000000..eb0efa9 --- /dev/null +++ b/drivers/clk/exynos/Kconfig @@ -0,0 +1,18 @@ +config CLK_EXYNOS + bool + select CLK + help + This enables support for common clock driver API on Samsung + Exynos SoCs. + +menu "Clock drivers for Exynos SoCs" + depends on CLK_EXYNOS + +config CLK_EXYNOS7420 + bool "Clock driver for Samsung's Exynos7420 SoC" + default y + help + This enables common clock driver support for platforms based + on Samsung Exynos7420 SoC. + +endmenu diff --git a/drivers/clk/exynos/Makefile b/drivers/clk/exynos/Makefile new file mode 100644 index 0000000..1df10fe --- /dev/null +++ b/drivers/clk/exynos/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2016 Samsung Electronics +# Thomas Abraham <thomas.ab@samsung.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += clk-pll.o +obj-$(CONFIG_CLK_EXYNOS7420) += clk-exynos7420.o diff --git a/drivers/clk/exynos/clk-exynos7420.c b/drivers/clk/exynos/clk-exynos7420.c new file mode 100644 index 0000000..bf5d0e6 --- /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, +}; diff --git a/drivers/clk/exynos/clk-pll.c b/drivers/clk/exynos/clk-pll.c new file mode 100644 index 0000000..27220c5 --- /dev/null +++ b/drivers/clk/exynos/clk-pll.c @@ -0,0 +1,33 @@ +/* + * Exynos PLL helper functions for clock drivers. + * Copyright (C) 2016 Samsung Electronics + * Thomas Abraham <thomas.ab@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <div64.h> + +#define PLL145X_MDIV_SHIFT 16 +#define PLL145X_MDIV_MASK 0x3ff +#define PLL145X_PDIV_SHIFT 8 +#define PLL145X_PDIV_MASK 0x3f +#define PLL145X_SDIV_SHIFT 0 +#define PLL145X_SDIV_MASK 0x7 + +unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq) +{ + unsigned long pll_con1 = readl(con1); + unsigned long mdiv, sdiv, pdiv; + uint64_t fvco = fin_freq; + + mdiv = (pll_con1 >> PLL145X_MDIV_SHIFT) & PLL145X_MDIV_MASK; + pdiv = (pll_con1 >> PLL145X_PDIV_SHIFT) & PLL145X_PDIV_MASK; + sdiv = (pll_con1 >> PLL145X_SDIV_SHIFT) & PLL145X_SDIV_MASK; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + return (unsigned long)fvco; +} diff --git a/drivers/clk/exynos/clk-pll.h b/drivers/clk/exynos/clk-pll.h new file mode 100644 index 0000000..631d035 --- /dev/null +++ b/drivers/clk/exynos/clk-pll.h @@ -0,0 +1,9 @@ +/* + * Exynos PLL helper functions for clock drivers. + * Copyright (C) 2016 Samsung Electronics + * Thomas Abraham <thomas.ab@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +unsigned long pll145x_get_rate(unsigned int *con1, unsigned long fin_freq); |