diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-20 20:18:12 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-20 20:18:12 -0700 |
commit | 0eff4589c36edd03d50b835d0768b2c2ef3f20bd (patch) | |
tree | f0a08e7ed4dac042d89d24bb4c79f66df70085ff /drivers/clk | |
parent | 087afe8aaf562dc7a53f2577049830d6a3245742 (diff) | |
parent | ef56b79b66faeeb0dc14213d3cc9e0534a960dee (diff) | |
download | linux-0eff4589c36edd03d50b835d0768b2c2ef3f20bd.tar.bz2 |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd:
"It's the usual big pile of driver updates and additions, but we do
have a couple core changes in here as well.
Core:
- CLK_IS_CRITICAL support has been added. This should allow drivers
to properly express that a certain clk should stay on even if their
prepare/enable count drops to 0 (and in turn the parents of these
clks should stay enabled).
- A clk registration API has been added, clk_hw_register(), and an OF
clk provider API has been added, of_clk_add_hw_provider(). These
APIs have been put in place to further split clk providers from clk
consumers, with the goal being to have clk providers never deal
with struct clk pointers at all. Conversion of provider drivers is
on going. clkdev has also gained support for registering clk_hw
pointers directly so we can convert drivers that don't use
devicetree.
New Drivers:
- Marvell ap806 and cp110 system controllers (with clks inside!)
- Hisilicon Hi3519 clock and reset controller
- Axis ARTPEC-6 clock controllers
- Oxford Semiconductor OXNAS clock controllers
- AXS10X I2S PLL
- Rockchip RK3399 clock and reset controller
Updates:
- MMC2 and UART2 clks on Samsung Exynos 3250, ACLK on Samsung Exynos
542x SoCs, and some more clk ID exporting for bus frequency scaling
- Proper BCM2835 PCM clk support and various other clks
- i.MX clk updates for i.MX6SX, i.MX7, and VF610
- Renesas updates for R-Car H3
- Tegra210 got updates for DisplayPort and HDMI 2.0
- Rockchip driver refactorings and fixes due to adding RK3399 support"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (139 commits)
clk: fix critical clock locking
clk: qcom: mmcc-8996: Remove clocks that should be controlled by RPM
clk: ingenic: Allow divider value to be divided
clk: sunxi: Add display and TCON0 clocks driver
clk: rockchip: drop old_rate calculation on pll rate changes
clk: rockchip: simplify GRF handling in pll clocks
clk: rockchip: lookup General Register Files in rockchip_clk_init
clk: rockchip: fix the rk3399 sdmmc sample / drv name
clk: mvebu: new driver for Armada CP110 system controller
dt-bindings: arm: add DT binding for Marvell CP110 system controller
clk: mvebu: new driver for Armada AP806 system controller
clk: hisilicon: add CRG driver for hi3519 soc
clk: hisilicon: export some hisilicon APIs to modules
reset: hisilicon: add reset controller driver for hisilicon SOCs
clk: bcm/kona: Do not use sizeof on pointer type
clk: qcom: msm8916: Fix crypto clock flags
clk: nxp: lpc18xx: Initialize clk_init_data::flags to 0
clk/axs10x: Add I2S PLL clock driver
clk: imx7d: fix ahb clock mux 1
clk: fix comment of devm_clk_hw_register()
...
Diffstat (limited to 'drivers/clk')
115 files changed, 6589 insertions, 1113 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 90518cd7fc9c..53ddba26578c 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -200,6 +200,12 @@ config COMMON_CLK_PXA config COMMON_CLK_PIC32 def_bool COMMON_CLK && MACH_PIC32 +config COMMON_CLK_OXNAS + bool "Clock driver for the OXNAS SoC Family" + select MFD_SYSCON + ---help--- + Support for the OXNAS SoC Family clocks. + source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 18e64bbeeaf4..dcc5e698ff6d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o +obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o @@ -51,6 +52,7 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ +obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ @@ -62,7 +64,7 @@ obj-$(CONFIG_MACH_PIC32) += microchip/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif -obj-$(CONFIG_PLAT_ORION) += mvebu/ +obj-y += mvebu/ obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ @@ -85,3 +87,4 @@ obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_ARCH_ZX) += zte/ obj-$(CONFIG_ARCH_ZYNQ) += zynq/ obj-$(CONFIG_H8300) += h8300/ +obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c index 819f5842fa66..8e20c8a76db7 100644 --- a/drivers/clk/at91/clk-h32mx.c +++ b/drivers/clk/at91/clk-h32mx.c @@ -114,7 +114,7 @@ static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np) h32mxclk->regmap = regmap; clk = clk_register(NULL, &h32mxclk->hw); - if (!clk) { + if (IS_ERR(clk)) { kfree(h32mxclk); return; } diff --git a/drivers/clk/axis/Makefile b/drivers/clk/axis/Makefile new file mode 100644 index 000000000000..628c9d3b9a02 --- /dev/null +++ b/drivers/clk/axis/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MACH_ARTPEC6) += clk-artpec6.o diff --git a/drivers/clk/axis/clk-artpec6.c b/drivers/clk/axis/clk-artpec6.c new file mode 100644 index 000000000000..ffc988b098e4 --- /dev/null +++ b/drivers/clk/axis/clk-artpec6.c @@ -0,0 +1,242 @@ +/* + * ARTPEC-6 clock initialization + * + * Copyright 2015-2016 Axis Comunications AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <dt-bindings/clock/axis,artpec6-clkctrl.h> + +#define NUM_I2S_CLOCKS 2 + +struct artpec6_clkctrl_drvdata { + struct clk *clk_table[ARTPEC6_CLK_NUMCLOCKS]; + void __iomem *syscon_base; + struct clk_onecell_data clk_data; + spinlock_t i2scfg_lock; +}; + +static struct artpec6_clkctrl_drvdata *clkdata; + +static const char *const i2s_clk_names[NUM_I2S_CLOCKS] = { + "i2s0", + "i2s1", +}; + +static const int i2s_clk_indexes[NUM_I2S_CLOCKS] = { + ARTPEC6_CLK_I2S0_CLK, + ARTPEC6_CLK_I2S1_CLK, +}; + +static void of_artpec6_clkctrl_setup(struct device_node *np) +{ + int i; + const char *sys_refclk_name; + u32 pll_mode, pll_m, pll_n; + struct clk **clks; + + /* Mandatory parent clock. */ + i = of_property_match_string(np, "clock-names", "sys_refclk"); + if (i < 0) + return; + + sys_refclk_name = of_clk_get_parent_name(np, i); + + clkdata = kzalloc(sizeof(*clkdata), GFP_KERNEL); + if (!clkdata) + return; + + clks = clkdata->clk_table; + + for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) + clks[i] = ERR_PTR(-EPROBE_DEFER); + + clkdata->syscon_base = of_iomap(np, 0); + BUG_ON(clkdata->syscon_base == NULL); + + /* Read PLL1 factors configured by boot strap pins. */ + pll_mode = (readl(clkdata->syscon_base) >> 6) & 3; + switch (pll_mode) { + case 0: /* DDR3-2133 mode */ + pll_m = 4; + pll_n = 85; + break; + case 1: /* DDR3-1866 mode */ + pll_m = 6; + pll_n = 112; + break; + case 2: /* DDR3-1600 mode */ + pll_m = 4; + pll_n = 64; + break; + case 3: /* DDR3-1333 mode */ + pll_m = 8; + pll_n = 106; + break; + } + + clks[ARTPEC6_CLK_CPU] = + clk_register_fixed_factor(NULL, "cpu", sys_refclk_name, 0, pll_n, + pll_m); + clks[ARTPEC6_CLK_CPU_PERIPH] = + clk_register_fixed_factor(NULL, "cpu_periph", "cpu", 0, 1, 2); + + /* EPROBE_DEFER on the apb_clock is not handled in amba devices. */ + clks[ARTPEC6_CLK_UART_PCLK] = + clk_register_fixed_factor(NULL, "uart_pclk", "cpu", 0, 1, 8); + clks[ARTPEC6_CLK_UART_REFCLK] = + clk_register_fixed_rate(NULL, "uart_ref", sys_refclk_name, 0, + 50000000); + + clks[ARTPEC6_CLK_SPI_PCLK] = + clk_register_fixed_factor(NULL, "spi_pclk", "cpu", 0, 1, 8); + clks[ARTPEC6_CLK_SPI_SSPCLK] = + clk_register_fixed_rate(NULL, "spi_sspclk", sys_refclk_name, 0, + 50000000); + + clks[ARTPEC6_CLK_DBG_PCLK] = + clk_register_fixed_factor(NULL, "dbg_pclk", "cpu", 0, 1, 8); + + clkdata->clk_data.clks = clkdata->clk_table; + clkdata->clk_data.clk_num = ARTPEC6_CLK_NUMCLOCKS; + + of_clk_add_provider(np, of_clk_src_onecell_get, &clkdata->clk_data); +} + +CLK_OF_DECLARE(artpec6_clkctrl, "axis,artpec6-clkctrl", + of_artpec6_clkctrl_setup); + +static int artpec6_clkctrl_probe(struct platform_device *pdev) +{ + int propidx; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct clk **clks = clkdata->clk_table; + const char *sys_refclk_name; + const char *i2s_refclk_name = NULL; + const char *frac_clk_name[2] = { NULL, NULL }; + const char *i2s_mux_parents[2]; + u32 muxreg; + int i; + int err = 0; + + /* Mandatory parent clock. */ + propidx = of_property_match_string(np, "clock-names", "sys_refclk"); + if (propidx < 0) + return -EINVAL; + + sys_refclk_name = of_clk_get_parent_name(np, propidx); + + /* Find clock names of optional parent clocks. */ + propidx = of_property_match_string(np, "clock-names", "i2s_refclk"); + if (propidx >= 0) + i2s_refclk_name = of_clk_get_parent_name(np, propidx); + + propidx = of_property_match_string(np, "clock-names", "frac_clk0"); + if (propidx >= 0) + frac_clk_name[0] = of_clk_get_parent_name(np, propidx); + propidx = of_property_match_string(np, "clock-names", "frac_clk1"); + if (propidx >= 0) + frac_clk_name[1] = of_clk_get_parent_name(np, propidx); + + spin_lock_init(&clkdata->i2scfg_lock); + + clks[ARTPEC6_CLK_NAND_CLKA] = + clk_register_fixed_factor(dev, "nand_clka", "cpu", 0, 1, 8); + clks[ARTPEC6_CLK_NAND_CLKB] = + clk_register_fixed_rate(dev, "nand_clkb", sys_refclk_name, 0, + 100000000); + clks[ARTPEC6_CLK_ETH_ACLK] = + clk_register_fixed_factor(dev, "eth_aclk", "cpu", 0, 1, 4); + clks[ARTPEC6_CLK_DMA_ACLK] = + clk_register_fixed_factor(dev, "dma_aclk", "cpu", 0, 1, 4); + clks[ARTPEC6_CLK_PTP_REF] = + clk_register_fixed_rate(dev, "ptp_ref", sys_refclk_name, 0, + 100000000); + clks[ARTPEC6_CLK_SD_PCLK] = + clk_register_fixed_rate(dev, "sd_pclk", sys_refclk_name, 0, + 100000000); + clks[ARTPEC6_CLK_SD_IMCLK] = + clk_register_fixed_rate(dev, "sd_imclk", sys_refclk_name, 0, + 100000000); + clks[ARTPEC6_CLK_I2S_HST] = + clk_register_fixed_factor(dev, "i2s_hst", "cpu", 0, 1, 8); + + for (i = 0; i < NUM_I2S_CLOCKS; ++i) { + if (i2s_refclk_name && frac_clk_name[i]) { + i2s_mux_parents[0] = frac_clk_name[i]; + i2s_mux_parents[1] = i2s_refclk_name; + + clks[i2s_clk_indexes[i]] = + clk_register_mux(dev, i2s_clk_names[i], + i2s_mux_parents, 2, + CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT, + clkdata->syscon_base + 0x14, i, 1, + 0, &clkdata->i2scfg_lock); + } else if (frac_clk_name[i]) { + /* Lock the mux for internal clock reference. */ + muxreg = readl(clkdata->syscon_base + 0x14); + muxreg &= ~BIT(i); + writel(muxreg, clkdata->syscon_base + 0x14); + clks[i2s_clk_indexes[i]] = + clk_register_fixed_factor(dev, i2s_clk_names[i], + frac_clk_name[i], 0, 1, + 1); + } else if (i2s_refclk_name) { + /* Lock the mux for external clock reference. */ + muxreg = readl(clkdata->syscon_base + 0x14); + muxreg |= BIT(i); + writel(muxreg, clkdata->syscon_base + 0x14); + clks[i2s_clk_indexes[i]] = + clk_register_fixed_factor(dev, i2s_clk_names[i], + i2s_refclk_name, 0, 1, 1); + } + } + + clks[ARTPEC6_CLK_I2C] = + clk_register_fixed_rate(dev, "i2c", sys_refclk_name, 0, 100000000); + + clks[ARTPEC6_CLK_SYS_TIMER] = + clk_register_fixed_rate(dev, "timer", sys_refclk_name, 0, + 100000000); + clks[ARTPEC6_CLK_FRACDIV_IN] = + clk_register_fixed_rate(dev, "fracdiv_in", sys_refclk_name, 0, + 600000000); + + for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) { + if (IS_ERR(clks[i]) && PTR_ERR(clks[i]) != -EPROBE_DEFER) { + dev_err(dev, + "Failed to register clock at index %d err=%ld\n", + i, PTR_ERR(clks[i])); + err = PTR_ERR(clks[i]); + } + } + + return err; +} + +static const struct of_device_id artpec_clkctrl_of_match[] = { + { .compatible = "axis,artpec6-clkctrl" }, + {} +}; + +static struct platform_driver artpec6_clkctrl_driver = { + .probe = artpec6_clkctrl_probe, + .driver = { + .name = "artpec6_clkctrl", + .of_match_table = artpec_clkctrl_of_match, + }, +}; + +builtin_platform_driver(artpec6_clkctrl_driver); diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile new file mode 100644 index 000000000000..01996b871b06 --- /dev/null +++ b/drivers/clk/axs10x/Makefile @@ -0,0 +1 @@ +obj-y += i2s_pll_clock.o diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c new file mode 100644 index 000000000000..411310d29581 --- /dev/null +++ b/drivers/clk/axs10x/i2s_pll_clock.c @@ -0,0 +1,228 @@ +/* + * Synopsys AXS10X SDP I2S PLL clock driver + * + * Copyright (C) 2016 Synopsys + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/of.h> + +/* PLL registers addresses */ +#define PLL_IDIV_REG 0x0 +#define PLL_FBDIV_REG 0x4 +#define PLL_ODIV0_REG 0x8 +#define PLL_ODIV1_REG 0xC + +struct i2s_pll_cfg { + unsigned int rate; + unsigned int idiv; + unsigned int fbdiv; + unsigned int odiv0; + unsigned int odiv1; +}; + +static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = { + /* 27 Mhz */ + { 1024000, 0x104, 0x451, 0x10E38, 0x2000 }, + { 1411200, 0x104, 0x596, 0x10D35, 0x2000 }, + { 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 }, + { 2048000, 0x82, 0x451, 0x10E38, 0x2000 }, + { 2822400, 0x82, 0x596, 0x10D35, 0x2000 }, + { 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 }, + { 2116800, 0x82, 0x3CF, 0x10C30, 0x2000 }, + { 2304000, 0x104, 0x79E, 0x10B2C, 0x2000 }, + { 0, 0, 0, 0, 0 }, +}; + +static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = { + /* 28.224 Mhz */ + { 1024000, 0x82, 0x105, 0x107DF, 0x2000 }, + { 1411200, 0x28A, 0x1, 0x10001, 0x2000 }, + { 1536000, 0xA28, 0x187, 0x10042, 0x2000 }, + { 2048000, 0x41, 0x105, 0x107DF, 0x2000 }, + { 2822400, 0x145, 0x1, 0x10001, 0x2000 }, + { 3072000, 0x514, 0x187, 0x10042, 0x2000 }, + { 2116800, 0x514, 0x42, 0x10001, 0x2000 }, + { 2304000, 0x619, 0x82, 0x10001, 0x2000 }, + { 0, 0, 0, 0, 0 }, +}; + +struct i2s_pll_clk { + void __iomem *base; + struct clk_hw hw; + struct device *dev; +}; + +static inline void i2s_pll_write(struct i2s_pll_clk *clk, unsigned int reg, + unsigned int val) +{ + writel_relaxed(val, clk->base + reg); +} + +static inline unsigned int i2s_pll_read(struct i2s_pll_clk *clk, + unsigned int reg) +{ + return readl_relaxed(clk->base + reg); +} + +static inline struct i2s_pll_clk *to_i2s_pll_clk(struct clk_hw *hw) +{ + return container_of(hw, struct i2s_pll_clk, hw); +} + +static inline unsigned int i2s_pll_get_value(unsigned int val) +{ + return (val & 0x3F) + ((val >> 6) & 0x3F); +} + +static const struct i2s_pll_cfg *i2s_pll_get_cfg(unsigned long prate) +{ + switch (prate) { + case 27000000: + return i2s_pll_cfg_27m; + case 28224000: + return i2s_pll_cfg_28m; + default: + return NULL; + } +} + +static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); + unsigned int idiv, fbdiv, odiv; + + idiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_IDIV_REG)); + fbdiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_FBDIV_REG)); + odiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_ODIV0_REG)); + + return ((parent_rate / idiv) * fbdiv) / odiv; +} + +static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); + const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate); + int i; + + if (!pll_cfg) { + dev_err(clk->dev, "invalid parent rate=%ld\n", *prate); + return -EINVAL; + } + + for (i = 0; pll_cfg[i].rate != 0; i++) + if (pll_cfg[i].rate == rate) + return rate; + + return -EINVAL; +} + +static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct i2s_pll_clk *clk = to_i2s_pll_clk(hw); + const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(parent_rate); + int i; + + if (!pll_cfg) { + dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate); + return -EINVAL; + } + + for (i = 0; pll_cfg[i].rate != 0; i++) { + if (pll_cfg[i].rate == rate) { + i2s_pll_write(clk, PLL_IDIV_REG, pll_cfg[i].idiv); + i2s_pll_write(clk, PLL_FBDIV_REG, pll_cfg[i].fbdiv); + i2s_pll_write(clk, PLL_ODIV0_REG, pll_cfg[i].odiv0); + i2s_pll_write(clk, PLL_ODIV1_REG, pll_cfg[i].odiv1); + return 0; + } + } + + dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, + parent_rate); + return -EINVAL; +} + +static const struct clk_ops i2s_pll_ops = { + .recalc_rate = i2s_pll_recalc_rate, + .round_rate = i2s_pll_round_rate, + .set_rate = i2s_pll_set_rate, +}; + +static int i2s_pll_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + const char *clk_name; + const char *parent_name; + struct clk *clk; + struct i2s_pll_clk *pll_clk; + struct clk_init_data init; + struct resource *mem; + + pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pll_clk->base = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->base)) + return PTR_ERR(pll_clk->base); + + clk_name = node->name; + init.name = clk_name; + init.ops = &i2s_pll_ops; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + pll_clk->hw.init = &init; + pll_clk->dev = dev; + + clk = devm_clk_register(dev, &pll_clk->hw); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register %s clock (%ld)\n", + clk_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + + return of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +static int i2s_pll_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static const struct of_device_id i2s_pll_clk_id[] = { + { .compatible = "snps,axs10x-i2s-pll-clock", }, + { }, +}; +MODULE_DEVICE_TABLE(of, i2s_pll_clk_id); + +static struct platform_driver i2s_pll_clk_driver = { + .driver = { + .name = "axs10x-i2s-pll-clock", + .of_match_table = i2s_pll_clk_id, + }, + .probe = i2s_pll_clk_probe, + .remove = i2s_pll_clk_remove, +}; +module_platform_driver(i2s_pll_clk_driver); + +MODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>"); +MODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index c74ed3fd496d..7a7970865c2d 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -12,9 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @@ -40,6 +37,7 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> #include <linux/clk/bcm2835.h> +#include <linux/debugfs.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -51,6 +49,7 @@ #define CM_GNRICCTL 0x000 #define CM_GNRICDIV 0x004 # define CM_DIV_FRAC_BITS 12 +# define CM_DIV_FRAC_MASK GENMASK(CM_DIV_FRAC_BITS - 1, 0) #define CM_VPUCTL 0x008 #define CM_VPUDIV 0x00c @@ -118,6 +117,8 @@ #define CM_SDCCTL 0x1a8 #define CM_SDCDIV 0x1ac #define CM_ARMCTL 0x1b0 +#define CM_AVEOCTL 0x1b8 +#define CM_AVEODIV 0x1bc #define CM_EMMCCTL 0x1c0 #define CM_EMMCDIV 0x1c4 @@ -128,6 +129,7 @@ # define CM_GATE BIT(CM_GATE_BIT) # define CM_BUSY BIT(7) # define CM_BUSYD BIT(8) +# define CM_FRAC BIT(9) # define CM_SRC_SHIFT 0 # define CM_SRC_BITS 4 # define CM_SRC_MASK 0xf @@ -297,11 +299,11 @@ struct bcm2835_cprman { struct device *dev; void __iomem *regs; - spinlock_t regs_lock; + spinlock_t regs_lock; /* spinlock for all clocks */ const char *osc_name; struct clk_onecell_data onecell; - struct clk *clks[BCM2835_CLOCK_COUNT]; + struct clk *clks[]; }; static inline void cprman_write(struct bcm2835_cprman *cprman, u32 reg, u32 val) @@ -314,6 +316,27 @@ static inline u32 cprman_read(struct bcm2835_cprman *cprman, u32 reg) return readl(cprman->regs + reg); } +static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base, + struct debugfs_reg32 *regs, size_t nregs, + struct dentry *dentry) +{ + struct dentry *regdump; + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(cprman->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return -ENOMEM; + + regset->regs = regs; + regset->nregs = nregs; + regset->base = cprman->regs + base; + + regdump = debugfs_create_regset32("regdump", S_IRUGO, dentry, + regset); + + return regdump ? 0 : -ENOMEM; +} + /* * These are fixed clocks. They're probably not all root clocks and it may * be possible to turn them on and off but until this is mapped out better @@ -377,132 +400,27 @@ struct bcm2835_pll_ana_bits { static const struct bcm2835_pll_ana_bits bcm2835_ana_default = { .mask0 = 0, .set0 = 0, - .mask1 = ~(A2W_PLL_KI_MASK | A2W_PLL_KP_MASK), + .mask1 = (u32)~(A2W_PLL_KI_MASK | A2W_PLL_KP_MASK), .set1 = (2 << A2W_PLL_KI_SHIFT) | (8 << A2W_PLL_KP_SHIFT), - .mask3 = ~A2W_PLL_KA_MASK, + .mask3 = (u32)~A2W_PLL_KA_MASK, .set3 = (2 << A2W_PLL_KA_SHIFT), .fb_prediv_mask = BIT(14), }; static const struct bcm2835_pll_ana_bits bcm2835_ana_pllh = { - .mask0 = ~(A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK), + .mask0 = (u32)~(A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK), .set0 = (2 << A2W_PLLH_KA_SHIFT) | (2 << A2W_PLLH_KI_LOW_SHIFT), - .mask1 = ~(A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK), + .mask1 = (u32)~(A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK), .set1 = (6 << A2W_PLLH_KP_SHIFT), .mask3 = 0, .set3 = 0, .fb_prediv_mask = BIT(11), }; -/* - * PLLA is the auxiliary PLL, used to drive the CCP2 (Compact Camera - * Port 2) transmitter clock. - * - * It is in the PX LDO power domain, which is on when the AUDIO domain - * is on. - */ -static const struct bcm2835_pll_data bcm2835_plla_data = { - .name = "plla", - .cm_ctrl_reg = CM_PLLA, - .a2w_ctrl_reg = A2W_PLLA_CTRL, - .frac_reg = A2W_PLLA_FRAC, - .ana_reg_base = A2W_PLLA_ANA0, - .reference_enable_mask = A2W_XOSC_CTRL_PLLA_ENABLE, - .lock_mask = CM_LOCK_FLOCKA, - - .ana = &bcm2835_ana_default, - - .min_rate = 600000000u, - .max_rate = 2400000000u, - .max_fb_rate = BCM2835_MAX_FB_RATE, -}; - -/* PLLB is used for the ARM's clock. */ -static const struct bcm2835_pll_data bcm2835_pllb_data = { - .name = "pllb", - .cm_ctrl_reg = CM_PLLB, - .a2w_ctrl_reg = A2W_PLLB_CTRL, - .frac_reg = A2W_PLLB_FRAC, - .ana_reg_base = A2W_PLLB_ANA0, - .reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE, - .lock_mask = CM_LOCK_FLOCKB, - - .ana = &bcm2835_ana_default, - - .min_rate = 600000000u, - .max_rate = 3000000000u, - .max_fb_rate = BCM2835_MAX_FB_RATE, -}; - -/* - * PLLC is the core PLL, used to drive the core VPU clock. - * - * It is in the PX LDO power domain, which is on when the AUDIO domain - * is on. -*/ -static const struct bcm2835_pll_data bcm2835_pllc_data = { - .name = "pllc", - .cm_ctrl_reg = CM_PLLC, - .a2w_ctrl_reg = A2W_PLLC_CTRL, - .frac_reg = A2W_PLLC_FRAC, - .ana_reg_base = A2W_PLLC_ANA0, - .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, - .lock_mask = CM_LOCK_FLOCKC, - - .ana = &bcm2835_ana_default, - - .min_rate = 600000000u, - .max_rate = 3000000000u, - .max_fb_rate = BCM2835_MAX_FB_RATE, -}; - -/* - * PLLD is the display PLL, used to drive DSI display panels. - * - * It is in the PX LDO power domain, which is on when the AUDIO domain - * is on. - */ -static const struct bcm2835_pll_data bcm2835_plld_data = { - .name = "plld", - .cm_ctrl_reg = CM_PLLD, - .a2w_ctrl_reg = A2W_PLLD_CTRL, - .frac_reg = A2W_PLLD_FRAC, - .ana_reg_base = A2W_PLLD_ANA0, - .reference_enable_mask = A2W_XOSC_CTRL_DDR_ENABLE, - .lock_mask = CM_LOCK_FLOCKD, - - .ana = &bcm2835_ana_default, - - .min_rate = 600000000u, - .max_rate = 2400000000u, - .max_fb_rate = BCM2835_MAX_FB_RATE, -}; - -/* - * PLLH is used to supply the pixel clock or the AUX clock for the TV - * encoder. - * - * It is in the HDMI power domain. - */ -static const struct bcm2835_pll_data bcm2835_pllh_data = { - "pllh", - .cm_ctrl_reg = CM_PLLH, - .a2w_ctrl_reg = A2W_PLLH_CTRL, - .frac_reg = A2W_PLLH_FRAC, - .ana_reg_base = A2W_PLLH_ANA0, - .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, - .lock_mask = CM_LOCK_FLOCKH, - - .ana = &bcm2835_ana_pllh, - - .min_rate = 600000000u, - .max_rate = 3000000000u, - .max_fb_rate = BCM2835_MAX_FB_RATE, -}; - struct bcm2835_pll_divider_data { const char *name; - const struct bcm2835_pll_data *source_pll; + const char *source_pll; + u32 cm_reg; u32 a2w_reg; @@ -511,124 +429,6 @@ struct bcm2835_pll_divider_data { u32 fixed_divider; }; -static const struct bcm2835_pll_divider_data bcm2835_plla_core_data = { - .name = "plla_core", - .source_pll = &bcm2835_plla_data, - .cm_reg = CM_PLLA, - .a2w_reg = A2W_PLLA_CORE, - .load_mask = CM_PLLA_LOADCORE, - .hold_mask = CM_PLLA_HOLDCORE, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_plla_per_data = { - .name = "plla_per", - .source_pll = &bcm2835_plla_data, - .cm_reg = CM_PLLA, - .a2w_reg = A2W_PLLA_PER, - .load_mask = CM_PLLA_LOADPER, - .hold_mask = CM_PLLA_HOLDPER, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllb_arm_data = { - .name = "pllb_arm", - .source_pll = &bcm2835_pllb_data, - .cm_reg = CM_PLLB, - .a2w_reg = A2W_PLLB_ARM, - .load_mask = CM_PLLB_LOADARM, - .hold_mask = CM_PLLB_HOLDARM, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllc_core0_data = { - .name = "pllc_core0", - .source_pll = &bcm2835_pllc_data, - .cm_reg = CM_PLLC, - .a2w_reg = A2W_PLLC_CORE0, - .load_mask = CM_PLLC_LOADCORE0, - .hold_mask = CM_PLLC_HOLDCORE0, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllc_core1_data = { - .name = "pllc_core1", .source_pll = &bcm2835_pllc_data, - .cm_reg = CM_PLLC, A2W_PLLC_CORE1, - .load_mask = CM_PLLC_LOADCORE1, - .hold_mask = CM_PLLC_HOLDCORE1, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllc_core2_data = { - .name = "pllc_core2", - .source_pll = &bcm2835_pllc_data, - .cm_reg = CM_PLLC, - .a2w_reg = A2W_PLLC_CORE2, - .load_mask = CM_PLLC_LOADCORE2, - .hold_mask = CM_PLLC_HOLDCORE2, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllc_per_data = { - .name = "pllc_per", - .source_pll = &bcm2835_pllc_data, - .cm_reg = CM_PLLC, - .a2w_reg = A2W_PLLC_PER, - .load_mask = CM_PLLC_LOADPER, - .hold_mask = CM_PLLC_HOLDPER, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_plld_core_data = { - .name = "plld_core", - .source_pll = &bcm2835_plld_data, - .cm_reg = CM_PLLD, - .a2w_reg = A2W_PLLD_CORE, - .load_mask = CM_PLLD_LOADCORE, - .hold_mask = CM_PLLD_HOLDCORE, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_plld_per_data = { - .name = "plld_per", - .source_pll = &bcm2835_plld_data, - .cm_reg = CM_PLLD, - .a2w_reg = A2W_PLLD_PER, - .load_mask = CM_PLLD_LOADPER, - .hold_mask = CM_PLLD_HOLDPER, - .fixed_divider = 1, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllh_rcal_data = { - .name = "pllh_rcal", - .source_pll = &bcm2835_pllh_data, - .cm_reg = CM_PLLH, - .a2w_reg = A2W_PLLH_RCAL, - .load_mask = CM_PLLH_LOADRCAL, - .hold_mask = 0, - .fixed_divider = 10, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllh_aux_data = { - .name = "pllh_aux", - .source_pll = &bcm2835_pllh_data, - .cm_reg = CM_PLLH, - .a2w_reg = A2W_PLLH_AUX, - .load_mask = CM_PLLH_LOADAUX, - .hold_mask = 0, - .fixed_divider = 10, -}; - -static const struct bcm2835_pll_divider_data bcm2835_pllh_pix_data = { - .name = "pllh_pix", - .source_pll = &bcm2835_pllh_data, - .cm_reg = CM_PLLH, - .a2w_reg = A2W_PLLH_PIX, - .load_mask = CM_PLLH_LOADPIX, - .hold_mask = 0, - .fixed_divider = 10, -}; - struct bcm2835_clock_data { const char *name; @@ -644,187 +444,14 @@ struct bcm2835_clock_data { u32 frac_bits; bool is_vpu_clock; + bool is_mash_clock; }; -static const char *const bcm2835_clock_per_parents[] = { - "gnd", - "xosc", - "testdebug0", - "testdebug1", - "plla_per", - "pllc_per", - "plld_per", - "pllh_aux", -}; - -static const char *const bcm2835_clock_vpu_parents[] = { - "gnd", - "xosc", - "testdebug0", - "testdebug1", - "plla_core", - "pllc_core0", - "plld_core", - "pllh_aux", - "pllc_core1", - "pllc_core2", -}; - -static const char *const bcm2835_clock_osc_parents[] = { - "gnd", - "xosc", - "testdebug0", - "testdebug1" -}; - -/* - * Used for a 1Mhz clock for the system clocksource, and also used by - * the watchdog timer and the camera pulse generator. - */ -static const struct bcm2835_clock_data bcm2835_clock_timer_data = { - .name = "timer", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), - .parents = bcm2835_clock_osc_parents, - .ctl_reg = CM_TIMERCTL, - .div_reg = CM_TIMERDIV, - .int_bits = 6, - .frac_bits = 12, -}; - -/* One Time Programmable Memory clock. Maximum 10Mhz. */ -static const struct bcm2835_clock_data bcm2835_clock_otp_data = { - .name = "otp", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), - .parents = bcm2835_clock_osc_parents, - .ctl_reg = CM_OTPCTL, - .div_reg = CM_OTPDIV, - .int_bits = 4, - .frac_bits = 0, -}; - -/* - * VPU clock. This doesn't have an enable bit, since it drives the - * bus for everything else, and is special so it doesn't need to be - * gated for rate changes. It is also known as "clk_audio" in various - * hardware documentation. - */ -static const struct bcm2835_clock_data bcm2835_clock_vpu_data = { - .name = "vpu", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), - .parents = bcm2835_clock_vpu_parents, - .ctl_reg = CM_VPUCTL, - .div_reg = CM_VPUDIV, - .int_bits = 12, - .frac_bits = 8, - .is_vpu_clock = true, -}; - -static const struct bcm2835_clock_data bcm2835_clock_v3d_data = { - .name = "v3d", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), - .parents = bcm2835_clock_vpu_parents, - .ctl_reg = CM_V3DCTL, - .div_reg = CM_V3DDIV, - .int_bits = 4, - .frac_bits = 8, -}; - -static const struct bcm2835_clock_data bcm2835_clock_isp_data = { - .name = "isp", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), - .parents = bcm2835_clock_vpu_parents, - .ctl_reg = CM_ISPCTL, - .div_reg = CM_ISPDIV, - .int_bits = 4, - .frac_bits = 8, -}; - -static const struct bcm2835_clock_data bcm2835_clock_h264_data = { - .name = "h264", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), - .parents = bcm2835_clock_vpu_parents, - .ctl_reg = CM_H264CTL, - .div_reg = CM_H264DIV, - .int_bits = 4, - .frac_bits = 8, -}; - -/* TV encoder clock. Only operating frequency is 108Mhz. */ -static const struct bcm2835_clock_data bcm2835_clock_vec_data = { - .name = "vec", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), - .parents = bcm2835_clock_per_parents, - .ctl_reg = CM_VECCTL, - .div_reg = CM_VECDIV, - .int_bits = 4, - .frac_bits = 0, -}; - -static const struct bcm2835_clock_data bcm2835_clock_uart_data = { - .name = "uart", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), - .parents = bcm2835_clock_per_parents, - .ctl_reg = CM_UARTCTL, - .div_reg = CM_UARTDIV, - .int_bits = 10, - .frac_bits = 12, -}; - -/* HDMI state machine */ -static const struct bcm2835_clock_data bcm2835_clock_hsm_data = { - .name = "hsm", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), - .parents = bcm2835_clock_per_parents, - .ctl_reg = CM_HSMCTL, - .div_reg = CM_HSMDIV, - .int_bits = 4, - .frac_bits = 8, -}; - -/* - * Secondary SDRAM clock. Used for low-voltage modes when the PLL in - * the SDRAM controller can't be used. - */ -static const struct bcm2835_clock_data bcm2835_clock_sdram_data = { - .name = "sdram", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), - .parents = bcm2835_clock_vpu_parents, - .ctl_reg = CM_SDCCTL, - .div_reg = CM_SDCDIV, - .int_bits = 6, - .frac_bits = 0, -}; - -/* Clock for the temperature sensor. Generally run at 2Mhz, max 5Mhz. */ -static const struct bcm2835_clock_data bcm2835_clock_tsens_data = { - .name = "tsens", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), - .parents = bcm2835_clock_osc_parents, - .ctl_reg = CM_TSENSCTL, - .div_reg = CM_TSENSDIV, - .int_bits = 5, - .frac_bits = 0, -}; - -/* Arasan EMMC clock */ -static const struct bcm2835_clock_data bcm2835_clock_emmc_data = { - .name = "emmc", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), - .parents = bcm2835_clock_per_parents, - .ctl_reg = CM_EMMCCTL, - .div_reg = CM_EMMCDIV, - .int_bits = 4, - .frac_bits = 8, -}; +struct bcm2835_gate_data { + const char *name; + const char *parent; -static const struct bcm2835_clock_data bcm2835_clock_pwm_data = { - .name = "pwm", - .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), - .parents = bcm2835_clock_per_parents, - .ctl_reg = CM_PWMCTL, - .div_reg = CM_PWMDIV, - .int_bits = 12, - .frac_bits = 12, + u32 ctl_reg; }; struct bcm2835_pll { @@ -910,8 +537,14 @@ static void bcm2835_pll_off(struct clk_hw *hw) struct bcm2835_cprman *cprman = pll->cprman; const struct bcm2835_pll_data *data = pll->data; - cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST); - cprman_write(cprman, data->a2w_ctrl_reg, A2W_PLL_CTRL_PWRDN); + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->cm_ctrl_reg, + cprman_read(cprman, data->cm_ctrl_reg) | + CM_PLL_ANARST); + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) | + A2W_PLL_CTRL_PWRDN); + spin_unlock(&cprman->regs_lock); } static int bcm2835_pll_on(struct clk_hw *hw) @@ -921,6 +554,10 @@ static int bcm2835_pll_on(struct clk_hw *hw) const struct bcm2835_pll_data *data = pll->data; ktime_t timeout; + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) & + ~A2W_PLL_CTRL_PWRDN); + /* Take the PLL out of reset. */ cprman_write(cprman, data->cm_ctrl_reg, cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST); @@ -1030,6 +667,36 @@ static int bcm2835_pll_set_rate(struct clk_hw *hw, return 0; } +static int bcm2835_pll_debug_init(struct clk_hw *hw, + struct dentry *dentry) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + struct debugfs_reg32 *regs; + + regs = devm_kzalloc(cprman->dev, 7 * sizeof(*regs), GFP_KERNEL); + if (!regs) + return -ENOMEM; + + regs[0].name = "cm_ctrl"; + regs[0].offset = data->cm_ctrl_reg; + regs[1].name = "a2w_ctrl"; + regs[1].offset = data->a2w_ctrl_reg; + regs[2].name = "frac"; + regs[2].offset = data->frac_reg; + regs[3].name = "ana0"; + regs[3].offset = data->ana_reg_base + 0 * 4; + regs[4].name = "ana1"; + regs[4].offset = data->ana_reg_base + 1 * 4; + regs[5].name = "ana2"; + regs[5].offset = data->ana_reg_base + 2 * 4; + regs[6].name = "ana3"; + regs[6].offset = data->ana_reg_base + 3 * 4; + + return bcm2835_debugfs_regset(cprman, 0, regs, 7, dentry); +} + static const struct clk_ops bcm2835_pll_clk_ops = { .is_prepared = bcm2835_pll_is_on, .prepare = bcm2835_pll_on, @@ -1037,6 +704,7 @@ static const struct clk_ops bcm2835_pll_clk_ops = { .recalc_rate = bcm2835_pll_get_rate, .set_rate = bcm2835_pll_set_rate, .round_rate = bcm2835_pll_round_rate, + .debug_init = bcm2835_pll_debug_init, }; struct bcm2835_pll_divider { @@ -1079,10 +747,12 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; + spin_lock(&cprman->regs_lock); cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + spin_unlock(&cprman->regs_lock); } static int bcm2835_pll_divider_on(struct clk_hw *hw) @@ -1091,12 +761,14 @@ static int bcm2835_pll_divider_on(struct clk_hw *hw) struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; + spin_lock(&cprman->regs_lock); cprman_write(cprman, data->a2w_reg, cprman_read(cprman, data->a2w_reg) & ~A2W_PLL_CHANNEL_DISABLE); cprman_write(cprman, data->cm_reg, cprman_read(cprman, data->cm_reg) & ~data->hold_mask); + spin_unlock(&cprman->regs_lock); return 0; } @@ -1124,6 +796,26 @@ static int bcm2835_pll_divider_set_rate(struct clk_hw *hw, return 0; } +static int bcm2835_pll_divider_debug_init(struct clk_hw *hw, + struct dentry *dentry) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + struct debugfs_reg32 *regs; + + regs = devm_kzalloc(cprman->dev, 7 * sizeof(*regs), GFP_KERNEL); + if (!regs) + return -ENOMEM; + + regs[0].name = "cm"; + regs[0].offset = data->cm_reg; + regs[1].name = "a2w"; + regs[1].offset = data->a2w_reg; + + return bcm2835_debugfs_regset(cprman, 0, regs, 2, dentry); +} + static const struct clk_ops bcm2835_pll_divider_clk_ops = { .is_prepared = bcm2835_pll_divider_is_on, .prepare = bcm2835_pll_divider_on, @@ -1131,6 +823,7 @@ static const struct clk_ops bcm2835_pll_divider_clk_ops = { .recalc_rate = bcm2835_pll_divider_get_rate, .set_rate = bcm2835_pll_divider_set_rate, .round_rate = bcm2835_pll_divider_round_rate, + .debug_init = bcm2835_pll_divider_debug_init, }; /* @@ -1170,7 +863,7 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw, GENMASK(CM_DIV_FRAC_BITS - data->frac_bits, 0) >> 1; u64 temp = (u64)parent_rate << CM_DIV_FRAC_BITS; u64 rem; - u32 div; + u32 div, mindiv, maxdiv; rem = do_div(temp, rate); div = temp; @@ -1180,10 +873,23 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw, div += unused_frac_mask + 1; div &= ~unused_frac_mask; - /* Clamp to the limits. */ - div = max(div, unused_frac_mask + 1); - div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, - CM_DIV_FRAC_BITS - data->frac_bits)); + /* different clamping limits apply for a mash clock */ + if (data->is_mash_clock) { + /* clamp to min divider of 2 */ + mindiv = 2 << CM_DIV_FRAC_BITS; + /* clamp to the highest possible integer divider */ + maxdiv = (BIT(data->int_bits) - 1) << CM_DIV_FRAC_BITS; + } else { + /* clamp to min divider of 1 */ + mindiv = 1 << CM_DIV_FRAC_BITS; + /* clamp to the highest possible fractional divider */ + maxdiv = GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, + CM_DIV_FRAC_BITS - data->frac_bits); + } + + /* apply the clamping limits */ + div = max_t(u32, div, mindiv); + div = min_t(u32, div, maxdiv); return div; } @@ -1277,14 +983,31 @@ static int bcm2835_clock_set_rate(struct clk_hw *hw, struct bcm2835_cprman *cprman = clock->cprman; const struct bcm2835_clock_data *data = clock->data; u32 div = bcm2835_clock_choose_div(hw, rate, parent_rate, false); + u32 ctl; + + spin_lock(&cprman->regs_lock); + + /* + * Setting up frac support + * + * In principle it is recommended to stop/start the clock first, + * but as we set CLK_SET_RATE_GATE during registration of the + * clock this requirement should be take care of by the + * clk-framework. + */ + ctl = cprman_read(cprman, data->ctl_reg) & ~CM_FRAC; + ctl |= (div & CM_DIV_FRAC_MASK) ? CM_FRAC : 0; + cprman_write(cprman, data->ctl_reg, ctl); cprman_write(cprman, data->div_reg, div); + spin_unlock(&cprman->regs_lock); + return 0; } static int bcm2835_clock_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) + struct clk_rate_request *req) { struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); struct clk_hw *parent, *best_parent = NULL; @@ -1342,6 +1065,30 @@ static u8 bcm2835_clock_get_parent(struct clk_hw *hw) return (src & CM_SRC_MASK) >> CM_SRC_SHIFT; } +static struct debugfs_reg32 bcm2835_debugfs_clock_reg32[] = { + { + .name = "ctl", + .offset = 0, + }, + { + .name = "div", + .offset = 4, + }, +}; + +static int bcm2835_clock_debug_init(struct clk_hw *hw, + struct dentry *dentry) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + return bcm2835_debugfs_regset( + cprman, data->ctl_reg, + bcm2835_debugfs_clock_reg32, + ARRAY_SIZE(bcm2835_debugfs_clock_reg32), + dentry); +} static const struct clk_ops bcm2835_clock_clk_ops = { .is_prepared = bcm2835_clock_is_on, @@ -1352,6 +1099,7 @@ static const struct clk_ops bcm2835_clock_clk_ops = { .determine_rate = bcm2835_clock_determine_rate, .set_parent = bcm2835_clock_set_parent, .get_parent = bcm2835_clock_get_parent, + .debug_init = bcm2835_clock_debug_init, }; static int bcm2835_vpu_clock_is_on(struct clk_hw *hw) @@ -1370,6 +1118,7 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = { .determine_rate = bcm2835_clock_determine_rate, .set_parent = bcm2835_clock_set_parent, .get_parent = bcm2835_clock_get_parent, + .debug_init = bcm2835_clock_debug_init, }; static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman, @@ -1418,7 +1167,7 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman, memset(&init, 0, sizeof(init)); - init.parent_names = &data->source_pll->name; + init.parent_names = &data->source_pll; init.num_parents = 1; init.name = divider_name; init.ops = &bcm2835_pll_divider_clk_ops; @@ -1501,14 +1250,559 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, return devm_clk_register(cprman->dev, &clock->hw); } +static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman, + const struct bcm2835_gate_data *data) +{ + return clk_register_gate(cprman->dev, data->name, data->parent, + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, + cprman->regs + data->ctl_reg, + CM_GATE_BIT, 0, &cprman->regs_lock); +} + +typedef struct clk *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman, + const void *data); +struct bcm2835_clk_desc { + bcm2835_clk_register clk_register; + const void *data; +}; + +/* assignment helper macros for different clock types */ +#define _REGISTER(f, ...) { .clk_register = (bcm2835_clk_register)f, \ + .data = __VA_ARGS__ } +#define REGISTER_PLL(...) _REGISTER(&bcm2835_register_pll, \ + &(struct bcm2835_pll_data) \ + {__VA_ARGS__}) +#define REGISTER_PLL_DIV(...) _REGISTER(&bcm2835_register_pll_divider, \ + &(struct bcm2835_pll_divider_data) \ + {__VA_ARGS__}) +#define REGISTER_CLK(...) _REGISTER(&bcm2835_register_clock, \ + &(struct bcm2835_clock_data) \ + {__VA_ARGS__}) +#define REGISTER_GATE(...) _REGISTER(&bcm2835_register_gate, \ + &(struct bcm2835_gate_data) \ + {__VA_ARGS__}) + +/* parent mux arrays plus helper macros */ + +/* main oscillator parent mux */ +static const char *const bcm2835_clock_osc_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1" +}; + +#define REGISTER_OSC_CLK(...) REGISTER_CLK( \ + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), \ + .parents = bcm2835_clock_osc_parents, \ + __VA_ARGS__) + +/* main peripherial parent mux */ +static const char *const bcm2835_clock_per_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "plla_per", + "pllc_per", + "plld_per", + "pllh_aux", +}; + +#define REGISTER_PER_CLK(...) REGISTER_CLK( \ + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), \ + .parents = bcm2835_clock_per_parents, \ + __VA_ARGS__) + +/* main vpu parent mux */ +static const char *const bcm2835_clock_vpu_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "plla_core", + "pllc_core0", + "plld_core", + "pllh_aux", + "pllc_core1", + "pllc_core2", +}; + +#define REGISTER_VPU_CLK(...) REGISTER_CLK( \ + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), \ + .parents = bcm2835_clock_vpu_parents, \ + __VA_ARGS__) + +/* + * the real definition of all the pll, pll_dividers and clocks + * these make use of the above REGISTER_* macros + */ +static const struct bcm2835_clk_desc clk_desc_array[] = { + /* the PLL + PLL dividers */ + + /* + * PLLA is the auxiliary PLL, used to drive the CCP2 + * (Compact Camera Port 2) transmitter clock. + * + * It is in the PX LDO power domain, which is on when the + * AUDIO domain is on. + */ + [BCM2835_PLLA] = REGISTER_PLL( + .name = "plla", + .cm_ctrl_reg = CM_PLLA, + .a2w_ctrl_reg = A2W_PLLA_CTRL, + .frac_reg = A2W_PLLA_FRAC, + .ana_reg_base = A2W_PLLA_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLA_ENABLE, + .lock_mask = CM_LOCK_FLOCKA, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 2400000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE), + [BCM2835_PLLA_CORE] = REGISTER_PLL_DIV( + .name = "plla_core", + .source_pll = "plla", + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_CORE, + .load_mask = CM_PLLA_LOADCORE, + .hold_mask = CM_PLLA_HOLDCORE, + .fixed_divider = 1), + [BCM2835_PLLA_PER] = REGISTER_PLL_DIV( + .name = "plla_per", + .source_pll = "plla", + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_PER, + .load_mask = CM_PLLA_LOADPER, + .hold_mask = CM_PLLA_HOLDPER, + .fixed_divider = 1), + [BCM2835_PLLA_DSI0] = REGISTER_PLL_DIV( + .name = "plla_dsi0", + .source_pll = "plla", + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_DSI0, + .load_mask = CM_PLLA_LOADDSI0, + .hold_mask = CM_PLLA_HOLDDSI0, + .fixed_divider = 1), + [BCM2835_PLLA_CCP2] = REGISTER_PLL_DIV( + .name = "plla_ccp2", + .source_pll = "plla", + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_CCP2, + .load_mask = CM_PLLA_LOADCCP2, + .hold_mask = CM_PLLA_HOLDCCP2, + .fixed_divider = 1), + + /* PLLB is used for the ARM's clock. */ + [BCM2835_PLLB] = REGISTER_PLL( + .name = "pllb", + .cm_ctrl_reg = CM_PLLB, + .a2w_ctrl_reg = A2W_PLLB_CTRL, + .frac_reg = A2W_PLLB_FRAC, + .ana_reg_base = A2W_PLLB_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE, + .lock_mask = CM_LOCK_FLOCKB, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE), + [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV( + .name = "pllb_arm", + .source_pll = "pllb", + .cm_reg = CM_PLLB, + .a2w_reg = A2W_PLLB_ARM, + .load_mask = CM_PLLB_LOADARM, + .hold_mask = CM_PLLB_HOLDARM, + .fixed_divider = 1), + + /* + * PLLC is the core PLL, used to drive the core VPU clock. + * + * It is in the PX LDO power domain, which is on when the + * AUDIO domain is on. + */ + [BCM2835_PLLC] = REGISTER_PLL( + .name = "pllc", + .cm_ctrl_reg = CM_PLLC, + .a2w_ctrl_reg = A2W_PLLC_CTRL, + .frac_reg = A2W_PLLC_FRAC, + .ana_reg_base = A2W_PLLC_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, + .lock_mask = CM_LOCK_FLOCKC, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE), + [BCM2835_PLLC_CORE0] = REGISTER_PLL_DIV( + .name = "pllc_core0", + .source_pll = "pllc", + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE0, + .load_mask = CM_PLLC_LOADCORE0, + .hold_mask = CM_PLLC_HOLDCORE0, + .fixed_divider = 1), + [BCM2835_PLLC_CORE1] = REGISTER_PLL_DIV( + .name = "pllc_core1", + .source_pll = "pllc", + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE1, + .load_mask = CM_PLLC_LOADCORE1, + .hold_mask = CM_PLLC_HOLDCORE1, + .fixed_divider = 1), + [BCM2835_PLLC_CORE2] = REGISTER_PLL_DIV( + .name = "pllc_core2", + .source_pll = "pllc", + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE2, + .load_mask = CM_PLLC_LOADCORE2, + .hold_mask = CM_PLLC_HOLDCORE2, + .fixed_divider = 1), + [BCM2835_PLLC_PER] = REGISTER_PLL_DIV( + .name = "pllc_per", + .source_pll = "pllc", + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_PER, + .load_mask = CM_PLLC_LOADPER, + .hold_mask = CM_PLLC_HOLDPER, + .fixed_divider = 1), + + /* + * PLLD is the display PLL, used to drive DSI display panels. + * + * It is in the PX LDO power domain, which is on when the + * AUDIO domain is on. + */ + [BCM2835_PLLD] = REGISTER_PLL( + .name = "plld", + .cm_ctrl_reg = CM_PLLD, + .a2w_ctrl_reg = A2W_PLLD_CTRL, + .frac_reg = A2W_PLLD_FRAC, + .ana_reg_base = A2W_PLLD_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_DDR_ENABLE, + .lock_mask = CM_LOCK_FLOCKD, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 2400000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE), + [BCM2835_PLLD_CORE] = REGISTER_PLL_DIV( + .name = "plld_core", + .source_pll = "plld", + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_CORE, + .load_mask = CM_PLLD_LOADCORE, + .hold_mask = CM_PLLD_HOLDCORE, + .fixed_divider = 1), + [BCM2835_PLLD_PER] = REGISTER_PLL_DIV( + .name = "plld_per", + .source_pll = "plld", + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_PER, + .load_mask = CM_PLLD_LOADPER, + .hold_mask = CM_PLLD_HOLDPER, + .fixed_divider = 1), + [BCM2835_PLLD_DSI0] = REGISTER_PLL_DIV( + .name = "plld_dsi0", + .source_pll = "plld", + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_DSI0, + .load_mask = CM_PLLD_LOADDSI0, + .hold_mask = CM_PLLD_HOLDDSI0, + .fixed_divider = 1), + [BCM2835_PLLD_DSI1] = REGISTER_PLL_DIV( + .name = "plld_dsi1", + .source_pll = "plld", + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_DSI1, + .load_mask = CM_PLLD_LOADDSI1, + .hold_mask = CM_PLLD_HOLDDSI1, + .fixed_divider = 1), + + /* + * PLLH is used to supply the pixel clock or the AUX clock for the + * TV encoder. + * + * It is in the HDMI power domain. + */ + [BCM2835_PLLH] = REGISTER_PLL( + "pllh", + .cm_ctrl_reg = CM_PLLH, + .a2w_ctrl_reg = A2W_PLLH_CTRL, + .frac_reg = A2W_PLLH_FRAC, + .ana_reg_base = A2W_PLLH_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, + .lock_mask = CM_LOCK_FLOCKH, + + .ana = &bcm2835_ana_pllh, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE), + [BCM2835_PLLH_RCAL] = REGISTER_PLL_DIV( + .name = "pllh_rcal", + .source_pll = "pllh", + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_RCAL, + .load_mask = CM_PLLH_LOADRCAL, + .hold_mask = 0, + .fixed_divider = 10), + [BCM2835_PLLH_AUX] = REGISTER_PLL_DIV( + .name = "pllh_aux", + .source_pll = "pllh", + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_AUX, + .load_mask = CM_PLLH_LOADAUX, + .hold_mask = 0, + .fixed_divider = 10), + [BCM2835_PLLH_PIX] = REGISTER_PLL_DIV( + .name = "pllh_pix", + .source_pll = "pllh", + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_PIX, + .load_mask = CM_PLLH_LOADPIX, + .hold_mask = 0, + .fixed_divider = 10), + + /* the clocks */ + + /* clocks with oscillator parent mux */ + + /* One Time Programmable Memory clock. Maximum 10Mhz. */ + [BCM2835_CLOCK_OTP] = REGISTER_OSC_CLK( + .name = "otp", + .ctl_reg = CM_OTPCTL, + .div_reg = CM_OTPDIV, + .int_bits = 4, + .frac_bits = 0), + /* + * Used for a 1Mhz clock for the system clocksource, and also used + * bythe watchdog timer and the camera pulse generator. + */ + [BCM2835_CLOCK_TIMER] = REGISTER_OSC_CLK( + .name = "timer", + .ctl_reg = CM_TIMERCTL, + .div_reg = CM_TIMERDIV, + .int_bits = 6, + .frac_bits = 12), + /* + * Clock for the temperature sensor. + * Generally run at 2Mhz, max 5Mhz. + */ + [BCM2835_CLOCK_TSENS] = REGISTER_OSC_CLK( + .name = "tsens", + .ctl_reg = CM_TSENSCTL, + .div_reg = CM_TSENSDIV, + .int_bits = 5, + .frac_bits = 0), + [BCM2835_CLOCK_TEC] = REGISTER_OSC_CLK( + .name = "tec", + .ctl_reg = CM_TECCTL, + .div_reg = CM_TECDIV, + .int_bits = 6, + .frac_bits = 0), + + /* clocks with vpu parent mux */ + [BCM2835_CLOCK_H264] = REGISTER_VPU_CLK( + .name = "h264", + .ctl_reg = CM_H264CTL, + .div_reg = CM_H264DIV, + .int_bits = 4, + .frac_bits = 8), + [BCM2835_CLOCK_ISP] = REGISTER_VPU_CLK( + .name = "isp", + .ctl_reg = CM_ISPCTL, + .div_reg = CM_ISPDIV, + .int_bits = 4, + .frac_bits = 8), + + /* + * Secondary SDRAM clock. Used for low-voltage modes when the PLL + * in the SDRAM controller can't be used. + */ + [BCM2835_CLOCK_SDRAM] = REGISTER_VPU_CLK( + .name = "sdram", + .ctl_reg = CM_SDCCTL, + .div_reg = CM_SDCDIV, + .int_bits = 6, + .frac_bits = 0), + [BCM2835_CLOCK_V3D] = REGISTER_VPU_CLK( + .name = "v3d", + .ctl_reg = CM_V3DCTL, + .div_reg = CM_V3DDIV, + .int_bits = 4, + .frac_bits = 8), + /* + * VPU clock. This doesn't have an enable bit, since it drives + * the bus for everything else, and is special so it doesn't need + * to be gated for rate changes. It is also known as "clk_audio" + * in various hardware documentation. + */ + [BCM2835_CLOCK_VPU] = REGISTER_VPU_CLK( + .name = "vpu", + .ctl_reg = CM_VPUCTL, + .div_reg = CM_VPUDIV, + .int_bits = 12, + .frac_bits = 8, + .is_vpu_clock = true), + + /* clocks with per parent mux */ + [BCM2835_CLOCK_AVEO] = REGISTER_PER_CLK( + .name = "aveo", + .ctl_reg = CM_AVEOCTL, + .div_reg = CM_AVEODIV, + .int_bits = 4, + .frac_bits = 0), + [BCM2835_CLOCK_CAM0] = REGISTER_PER_CLK( + .name = "cam0", + .ctl_reg = CM_CAM0CTL, + .div_reg = CM_CAM0DIV, + .int_bits = 4, + .frac_bits = 8), + [BCM2835_CLOCK_CAM1] = REGISTER_PER_CLK( + .name = "cam1", + .ctl_reg = CM_CAM1CTL, + .div_reg = CM_CAM1DIV, + .int_bits = 4, + .frac_bits = 8), + [BCM2835_CLOCK_DFT] = REGISTER_PER_CLK( + .name = "dft", + .ctl_reg = CM_DFTCTL, + .div_reg = CM_DFTDIV, + .int_bits = 5, + .frac_bits = 0), + [BCM2835_CLOCK_DPI] = REGISTER_PER_CLK( + .name = "dpi", + .ctl_reg = CM_DPICTL, + .div_reg = CM_DPIDIV, + .int_bits = 4, + .frac_bits = 8), + + /* Arasan EMMC clock */ + [BCM2835_CLOCK_EMMC] = REGISTER_PER_CLK( + .name = "emmc", + .ctl_reg = CM_EMMCCTL, + .div_reg = CM_EMMCDIV, + .int_bits = 4, + .frac_bits = 8), + + /* General purpose (GPIO) clocks */ + [BCM2835_CLOCK_GP0] = REGISTER_PER_CLK( + .name = "gp0", + .ctl_reg = CM_GP0CTL, + .div_reg = CM_GP0DIV, + .int_bits = 12, + .frac_bits = 12, + .is_mash_clock = true), + [BCM2835_CLOCK_GP1] = REGISTER_PER_CLK( + .name = "gp1", + .ctl_reg = CM_GP1CTL, + .div_reg = CM_GP1DIV, + .int_bits = 12, + .frac_bits = 12, + .is_mash_clock = true), + [BCM2835_CLOCK_GP2] = REGISTER_PER_CLK( + .name = "gp2", + .ctl_reg = CM_GP2CTL, + .div_reg = CM_GP2DIV, + .int_bits = 12, + .frac_bits = 12), + + /* HDMI state machine */ + [BCM2835_CLOCK_HSM] = REGISTER_PER_CLK( + .name = "hsm", + .ctl_reg = CM_HSMCTL, + .div_reg = CM_HSMDIV, + .int_bits = 4, + .frac_bits = 8), + [BCM2835_CLOCK_PCM] = REGISTER_PER_CLK( + .name = "pcm", + .ctl_reg = CM_PCMCTL, + .div_reg = CM_PCMDIV, + .int_bits = 12, + .frac_bits = 12, + .is_mash_clock = true), + [BCM2835_CLOCK_PWM] = REGISTER_PER_CLK( + .name = "pwm", + .ctl_reg = CM_PWMCTL, + .div_reg = CM_PWMDIV, + .int_bits = 12, + .frac_bits = 12, + .is_mash_clock = true), + [BCM2835_CLOCK_SLIM] = REGISTER_PER_CLK( + .name = "slim", + .ctl_reg = CM_SLIMCTL, + .div_reg = CM_SLIMDIV, + .int_bits = 12, + .frac_bits = 12, + .is_mash_clock = true), + [BCM2835_CLOCK_SMI] = REGISTER_PER_CLK( + .name = "smi", + .ctl_reg = CM_SMICTL, + .div_reg = CM_SMIDIV, + .int_bits = 4, + .frac_bits = 8), + [BCM2835_CLOCK_UART] = REGISTER_PER_CLK( + .name = "uart", + .ctl_reg = CM_UARTCTL, + .div_reg = CM_UARTDIV, + .int_bits = 10, + .frac_bits = 12), + + /* TV encoder clock. Only operating frequency is 108Mhz. */ + [BCM2835_CLOCK_VEC] = REGISTER_PER_CLK( + .name = "vec", + .ctl_reg = CM_VECCTL, + .div_reg = CM_VECDIV, + .int_bits = 4, + .frac_bits = 0), + + /* dsi clocks */ + [BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK( + .name = "dsi0e", + .ctl_reg = CM_DSI0ECTL, + .div_reg = CM_DSI0EDIV, + .int_bits = 4, + .frac_bits = 8), + [BCM2835_CLOCK_DSI1E] = REGISTER_PER_CLK( + .name = "dsi1e", + .ctl_reg = CM_DSI1ECTL, + .div_reg = CM_DSI1EDIV, + .int_bits = 4, + .frac_bits = 8), + + /* the gates */ + + /* + * CM_PERIICTL (and CM_PERIACTL, CM_SYSCTL and CM_VPUCTL if + * you have the debug bit set in the power manager, which we + * don't bother exposing) are individual gates off of the + * non-stop vpu clock. + */ + [BCM2835_CLOCK_PERI_IMAGE] = REGISTER_GATE( + .name = "peri_image", + .parent = "vpu", + .ctl_reg = CM_PERIICTL), +}; + static int bcm2835_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct clk **clks; struct bcm2835_cprman *cprman; struct resource *res; + const struct bcm2835_clk_desc *desc; + const size_t asize = ARRAY_SIZE(clk_desc_array); + size_t i; - cprman = devm_kzalloc(dev, sizeof(*cprman), GFP_KERNEL); + cprman = devm_kzalloc(dev, + sizeof(*cprman) + asize * sizeof(*clks), + GFP_KERNEL); if (!cprman) return -ENOMEM; @@ -1525,80 +1819,15 @@ static int bcm2835_clk_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cprman); - cprman->onecell.clk_num = BCM2835_CLOCK_COUNT; + cprman->onecell.clk_num = asize; cprman->onecell.clks = cprman->clks; clks = cprman->clks; - clks[BCM2835_PLLA] = bcm2835_register_pll(cprman, &bcm2835_plla_data); - clks[BCM2835_PLLB] = bcm2835_register_pll(cprman, &bcm2835_pllb_data); - clks[BCM2835_PLLC] = bcm2835_register_pll(cprman, &bcm2835_pllc_data); - clks[BCM2835_PLLD] = bcm2835_register_pll(cprman, &bcm2835_plld_data); - clks[BCM2835_PLLH] = bcm2835_register_pll(cprman, &bcm2835_pllh_data); - - clks[BCM2835_PLLA_CORE] = - bcm2835_register_pll_divider(cprman, &bcm2835_plla_core_data); - clks[BCM2835_PLLA_PER] = - bcm2835_register_pll_divider(cprman, &bcm2835_plla_per_data); - clks[BCM2835_PLLC_CORE0] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core0_data); - clks[BCM2835_PLLC_CORE1] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core1_data); - clks[BCM2835_PLLC_CORE2] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core2_data); - clks[BCM2835_PLLC_PER] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllc_per_data); - clks[BCM2835_PLLD_CORE] = - bcm2835_register_pll_divider(cprman, &bcm2835_plld_core_data); - clks[BCM2835_PLLD_PER] = - bcm2835_register_pll_divider(cprman, &bcm2835_plld_per_data); - clks[BCM2835_PLLH_RCAL] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllh_rcal_data); - clks[BCM2835_PLLH_AUX] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllh_aux_data); - clks[BCM2835_PLLH_PIX] = - bcm2835_register_pll_divider(cprman, &bcm2835_pllh_pix_data); - - clks[BCM2835_CLOCK_TIMER] = - bcm2835_register_clock(cprman, &bcm2835_clock_timer_data); - clks[BCM2835_CLOCK_OTP] = - bcm2835_register_clock(cprman, &bcm2835_clock_otp_data); - clks[BCM2835_CLOCK_TSENS] = - bcm2835_register_clock(cprman, &bcm2835_clock_tsens_data); - clks[BCM2835_CLOCK_VPU] = - bcm2835_register_clock(cprman, &bcm2835_clock_vpu_data); - clks[BCM2835_CLOCK_V3D] = - bcm2835_register_clock(cprman, &bcm2835_clock_v3d_data); - clks[BCM2835_CLOCK_ISP] = - bcm2835_register_clock(cprman, &bcm2835_clock_isp_data); - clks[BCM2835_CLOCK_H264] = - bcm2835_register_clock(cprman, &bcm2835_clock_h264_data); - clks[BCM2835_CLOCK_V3D] = - bcm2835_register_clock(cprman, &bcm2835_clock_v3d_data); - clks[BCM2835_CLOCK_SDRAM] = - bcm2835_register_clock(cprman, &bcm2835_clock_sdram_data); - clks[BCM2835_CLOCK_UART] = - bcm2835_register_clock(cprman, &bcm2835_clock_uart_data); - clks[BCM2835_CLOCK_VEC] = - bcm2835_register_clock(cprman, &bcm2835_clock_vec_data); - clks[BCM2835_CLOCK_HSM] = - bcm2835_register_clock(cprman, &bcm2835_clock_hsm_data); - clks[BCM2835_CLOCK_EMMC] = - bcm2835_register_clock(cprman, &bcm2835_clock_emmc_data); - - /* - * CM_PERIICTL (and CM_PERIACTL, CM_SYSCTL and CM_VPUCTL if - * you have the debug bit set in the power manager, which we - * don't bother exposing) are individual gates off of the - * non-stop vpu clock. - */ - clks[BCM2835_CLOCK_PERI_IMAGE] = - clk_register_gate(dev, "peri_image", "vpu", - CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, - cprman->regs + CM_PERIICTL, CM_GATE_BIT, - 0, &cprman->regs_lock); - - clks[BCM2835_CLOCK_PWM] = - bcm2835_register_clock(cprman, &bcm2835_clock_pwm_data); + for (i = 0; i < asize; i++) { + desc = &clk_desc_array[i]; + if (desc->clk_register && desc->data) + clks[i] = desc->clk_register(cprman, desc->data); + } return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &cprman->onecell); diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index deaa7f962b84..526b0b0e9a9f 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -577,7 +577,8 @@ static u32 *parent_process(const char *clocks[], * selector is not required, but we allocate space for the * array anyway to keep things simple. */ - parent_names = kmalloc(parent_count * sizeof(parent_names), GFP_KERNEL); + parent_names = kmalloc_array(parent_count, sizeof(*parent_names), + GFP_KERNEL); if (!parent_names) { pr_err("%s: error allocating %u parent names\n", __func__, parent_count); diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index ff4ef4f1df62..1f60b02416a7 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c @@ -107,16 +107,15 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, writel(tmp, base + CLPS711X_SYSCON1); clps711x_clk->clks[CLPS711X_CLK_DUMMY] = - clk_register_fixed_rate(NULL, "dummy", NULL, CLK_IS_ROOT, 0); + clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0); clps711x_clk->clks[CLPS711X_CLK_CPU] = - clk_register_fixed_rate(NULL, "cpu", NULL, CLK_IS_ROOT, f_cpu); + clk_register_fixed_rate(NULL, "cpu", NULL, 0, f_cpu); clps711x_clk->clks[CLPS711X_CLK_BUS] = - clk_register_fixed_rate(NULL, "bus", NULL, CLK_IS_ROOT, f_bus); + clk_register_fixed_rate(NULL, "bus", NULL, 0, f_bus); clps711x_clk->clks[CLPS711X_CLK_PLL] = - clk_register_fixed_rate(NULL, "pll", NULL, CLK_IS_ROOT, f_pll); + clk_register_fixed_rate(NULL, "pll", NULL, 0, f_pll); clps711x_clk->clks[CLPS711X_CLK_TIMERREF] = - clk_register_fixed_rate(NULL, "timer_ref", NULL, CLK_IS_ROOT, - f_tim); + clk_register_fixed_rate(NULL, "timer_ref", NULL, 0, f_tim); clps711x_clk->clks[CLPS711X_CLK_TIMER1] = clk_register_divider_table(NULL, "timer1", "timer_ref", 0, base + CLPS711X_SYSCON1, 5, 1, 0, @@ -126,10 +125,9 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, base + CLPS711X_SYSCON1, 7, 1, 0, timer_div_table, &clps711x_clk->lock); clps711x_clk->clks[CLPS711X_CLK_PWM] = - clk_register_fixed_rate(NULL, "pwm", NULL, CLK_IS_ROOT, f_pwm); + clk_register_fixed_rate(NULL, "pwm", NULL, 0, f_pwm); clps711x_clk->clks[CLPS711X_CLK_SPIREF] = - clk_register_fixed_rate(NULL, "spi_ref", NULL, CLK_IS_ROOT, - f_spi); + clk_register_fixed_rate(NULL, "spi_ref", NULL, 0, f_spi); clps711x_clk->clks[CLPS711X_CLK_SPI] = clk_register_divider_table(NULL, "spi", "spi_ref", 0, base + CLPS711X_SYSCON1, 16, 2, 0, @@ -137,8 +135,7 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, clps711x_clk->clks[CLPS711X_CLK_UART] = clk_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); clps711x_clk->clks[CLPS711X_CLK_TICK] = - clk_register_fixed_rate(NULL, "tick", NULL, CLK_IS_ROOT, 64); - + clk_register_fixed_rate(NULL, "tick", NULL, 0, 64); for (i = 0; i < CLPS711X_CLK_MAX; i++) if (IS_ERR(clps711x_clk->clks[i])) pr_err("clk %i: register failed with %ld\n", diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 1f903e1f86a2..00269de2f390 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -151,6 +151,33 @@ static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, return rate_ops->set_rate(rate_hw, rate, parent_rate); } +static int clk_composite_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 index) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *rate_hw = composite->rate_hw; + struct clk_hw *mux_hw = composite->mux_hw; + unsigned long temp_rate; + + __clk_hw_set_clk(rate_hw, hw); + __clk_hw_set_clk(mux_hw, hw); + + temp_rate = rate_ops->recalc_rate(rate_hw, parent_rate); + if (temp_rate > rate) { + rate_ops->set_rate(rate_hw, rate, parent_rate); + mux_ops->set_parent(mux_hw, index); + } else { + mux_ops->set_parent(mux_hw, index); + rate_ops->set_rate(rate_hw, rate, parent_rate); + } + + return 0; +} + static int clk_composite_is_enabled(struct clk_hw *hw) { struct clk_composite *composite = to_clk_composite(hw); @@ -184,17 +211,18 @@ static void clk_composite_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } -struct clk *clk_register_composite(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags) { - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; struct clk_composite *composite; struct clk_ops *clk_composite_ops; + int ret; composite = kzalloc(sizeof(*composite), GFP_KERNEL); if (!composite) @@ -204,12 +232,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name, init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; + hw = &composite->hw; clk_composite_ops = &composite->ops; if (mux_hw && mux_ops) { if (!mux_ops->get_parent) { - clk = ERR_PTR(-EINVAL); + hw = ERR_PTR(-EINVAL); goto err; } @@ -224,7 +253,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, if (rate_hw && rate_ops) { if (!rate_ops->recalc_rate) { - clk = ERR_PTR(-EINVAL); + hw = ERR_PTR(-EINVAL); goto err; } clk_composite_ops->recalc_rate = clk_composite_recalc_rate; @@ -250,10 +279,16 @@ struct clk *clk_register_composite(struct device *dev, const char *name, composite->rate_ops = rate_ops; } + if (mux_hw && mux_ops && rate_hw && rate_ops) { + if (mux_ops->set_parent && rate_ops->set_rate) + clk_composite_ops->set_rate_and_parent = + clk_composite_set_rate_and_parent; + } + if (gate_hw && gate_ops) { if (!gate_ops->is_enabled || !gate_ops->enable || !gate_ops->disable) { - clk = ERR_PTR(-EINVAL); + hw = ERR_PTR(-EINVAL); goto err; } @@ -267,22 +302,56 @@ struct clk *clk_register_composite(struct device *dev, const char *name, init.ops = clk_composite_ops; composite->hw.init = &init; - clk = clk_register(dev, &composite->hw); - if (IS_ERR(clk)) + ret = clk_hw_register(dev, hw); + if (ret) { + hw = ERR_PTR(ret); goto err; + } if (composite->mux_hw) - composite->mux_hw->clk = clk; + composite->mux_hw->clk = hw->clk; if (composite->rate_hw) - composite->rate_hw->clk = clk; + composite->rate_hw->clk = hw->clk; if (composite->gate_hw) - composite->gate_hw->clk = clk; + composite->gate_hw->clk = hw->clk; - return clk; + return hw; err: kfree(composite); - return clk; + return hw; +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char * const *parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_composite(dev, name, parent_names, num_parents, + mux_hw, mux_ops, rate_hw, rate_ops, gate_hw, gate_ops, + flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; +} + +void clk_unregister_composite(struct clk *clk) +{ + struct clk_composite *composite; + struct clk_hw *hw; + + hw = __clk_get_hw(clk); + if (!hw) + return; + + composite = to_clk_composite(hw); + + clk_unregister(clk); + kfree(composite); } diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 00e035b51c69..a0f55bc1ad3d 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -426,15 +426,16 @@ const struct clk_ops clk_divider_ro_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ro_ops); -static struct clk *_register_divider(struct device *dev, const char *name, +static struct clk_hw *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, const struct clk_div_table *table, spinlock_t *lock) { struct clk_divider *div; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { if (width + shift > 16) { @@ -467,12 +468,14 @@ static struct clk *_register_divider(struct device *dev, const char *name, div->table = table; /* register the clock */ - clk = clk_register(dev, &div->hw); - - if (IS_ERR(clk)) + hw = &div->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(div); + hw = ERR_PTR(ret); + } - return clk; + return hw; } /** @@ -492,12 +495,39 @@ struct clk *clk_register_divider(struct device *dev, const char *name, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, spinlock_t *lock) { - return _register_divider(dev, name, parent_name, flags, reg, shift, + struct clk_hw *hw; + + hw = _register_divider(dev, name, parent_name, flags, reg, shift, width, clk_divider_flags, NULL, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_divider); /** + * clk_hw_register_divider - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ +struct clk_hw *clk_hw_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, NULL, lock); +} +EXPORT_SYMBOL_GPL(clk_hw_register_divider); + +/** * clk_register_divider_table - register a table based divider clock with * the clock framework * @dev: device registering this clock @@ -517,11 +547,41 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, u8 clk_divider_flags, const struct clk_div_table *table, spinlock_t *lock) { - return _register_divider(dev, name, parent_name, flags, reg, shift, + struct clk_hw *hw; + + hw = _register_divider(dev, name, parent_name, flags, reg, shift, width, clk_divider_flags, table, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_divider_table); +/** + * clk_hw_register_divider_table - register a table based divider clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @table: array of divider/value pairs ending with a div set to 0 + * @lock: shared register lock for this clock + */ +struct clk_hw *clk_hw_register_divider_table(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, table, lock); +} +EXPORT_SYMBOL_GPL(clk_hw_register_divider_table); + void clk_unregister_divider(struct clk *clk) { struct clk_divider *div; @@ -537,3 +597,18 @@ void clk_unregister_divider(struct clk *clk) kfree(div); } EXPORT_SYMBOL_GPL(clk_unregister_divider); + +/** + * clk_hw_unregister_divider - unregister a clk divider + * @hw: hardware-specific clock data to unregister + */ +void clk_hw_unregister_divider(struct clk_hw *hw) +{ + struct clk_divider *div; + + div = to_clk_divider(hw); + + clk_hw_unregister(hw); + kfree(div); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_divider); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 053448e2453d..75cd6c792cb8 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -68,13 +68,14 @@ const struct clk_ops clk_fixed_factor_ops = { }; EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); -struct clk *clk_register_fixed_factor(struct device *dev, const char *name, - const char *parent_name, unsigned long flags, +struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { struct clk_fixed_factor *fix; struct clk_init_data init; - struct clk *clk; + struct clk_hw *hw; + int ret; fix = kmalloc(sizeof(*fix), GFP_KERNEL); if (!fix) @@ -91,12 +92,28 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, init.parent_names = &parent_name; init.num_parents = 1; - clk = clk_register(dev, &fix->hw); - - if (IS_ERR(clk)) + hw = &fix->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(fix); + hw = ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor); + +struct clk *clk_register_fixed_factor(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + struct clk_hw *hw; - return clk; + hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult, + div); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_fixed_factor); @@ -113,6 +130,17 @@ void clk_unregister_fixed_factor(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor); +void clk_hw_unregister_fixed_factor(struct clk_hw *hw) +{ + struct clk_fixed_factor *fix; + + fix = to_clk_fixed_factor(hw); + + clk_hw_unregister(hw); + kfree(fix); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor); + #ifdef CONFIG_OF /** * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index cd9dc925b3f8..8e4453eb54e8 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -45,8 +45,8 @@ const struct clk_ops clk_fixed_rate_ops = { EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); /** - * clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the - * clock framework + * clk_hw_register_fixed_rate_with_accuracy - register fixed-rate clock with + * the clock framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of clock's parent @@ -54,13 +54,14 @@ EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); * @fixed_rate: non-adjustable clock rate * @fixed_accuracy: non-adjustable clock rate */ -struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, +struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate, unsigned long fixed_accuracy) { struct clk_fixed_rate *fixed; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; /* allocate fixed-rate clock */ fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); @@ -79,22 +80,49 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, fixed->hw.init = &init; /* register the clock */ - clk = clk_register(dev, &fixed->hw); - if (IS_ERR(clk)) + hw = &fixed->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(fixed); + hw = ERR_PTR(ret); + } - return clk; + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_rate_with_accuracy); + +struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned long fixed_rate, unsigned long fixed_accuracy) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fixed_rate_with_accuracy(dev, name, parent_name, + flags, fixed_rate, fixed_accuracy); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_fixed_rate_with_accuracy); /** - * clk_register_fixed_rate - register fixed-rate clock with the clock framework + * clk_hw_register_fixed_rate - register fixed-rate clock with the clock + * framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of clock's parent * @flags: framework-specific flags * @fixed_rate: non-adjustable clock rate */ +struct clk_hw *clk_hw_register_fixed_rate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned long fixed_rate) +{ + return clk_hw_register_fixed_rate_with_accuracy(dev, name, parent_name, + flags, fixed_rate, 0); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_rate); + struct clk *clk_register_fixed_rate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate) diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 1abcd76b4993..aab904618eb6 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -116,14 +116,15 @@ const struct clk_ops clk_fractional_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_fractional_divider_ops); -struct clk *clk_register_fractional_divider(struct device *dev, +struct clk_hw *clk_hw_register_fractional_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, u8 clk_divider_flags, spinlock_t *lock) { struct clk_fractional_divider *fd; struct clk_init_data init; - struct clk *clk; + struct clk_hw *hw; + int ret; fd = kzalloc(sizeof(*fd), GFP_KERNEL); if (!fd) @@ -146,10 +147,39 @@ struct clk *clk_register_fractional_divider(struct device *dev, fd->lock = lock; fd->hw.init = &init; - clk = clk_register(dev, &fd->hw); - if (IS_ERR(clk)) + hw = &fd->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(fd); + hw = ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_fractional_divider); - return clk; +struct clk *clk_register_fractional_divider(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, + u8 clk_divider_flags, spinlock_t *lock) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fractional_divider(dev, name, parent_name, flags, + reg, mshift, mwidth, nshift, nwidth, clk_divider_flags, + lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_fractional_divider); + +void clk_hw_unregister_fractional_divider(struct clk_hw *hw) +{ + struct clk_fractional_divider *fd; + + fd = to_clk_fd(hw); + + clk_hw_unregister(hw); + kfree(fd); +} diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index d0d8ec8e1f1b..4e691e35483a 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -110,7 +110,7 @@ const struct clk_ops clk_gate_ops = { EXPORT_SYMBOL_GPL(clk_gate_ops); /** - * clk_register_gate - register a gate clock with the clock framework + * clk_hw_register_gate - register a gate clock with the clock framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of this clock's parent @@ -120,14 +120,15 @@ EXPORT_SYMBOL_GPL(clk_gate_ops); * @clk_gate_flags: gate-specific flags for this clock * @lock: shared register lock for this clock */ -struct clk *clk_register_gate(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) { struct clk_gate *gate; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { if (bit_idx > 15) { @@ -154,12 +155,29 @@ struct clk *clk_register_gate(struct device *dev, const char *name, gate->lock = lock; gate->hw.init = &init; - clk = clk_register(dev, &gate->hw); - - if (IS_ERR(clk)) + hw = &gate->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(gate); + hw = ERR_PTR(ret); + } - return clk; + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_gate); + +struct clk *clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct clk_hw *hw; + + hw = clk_hw_register_gate(dev, name, parent_name, flags, reg, + bit_idx, clk_gate_flags, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_gate); @@ -178,3 +196,14 @@ void clk_unregister_gate(struct clk *clk) kfree(gate); } EXPORT_SYMBOL_GPL(clk_unregister_gate); + +void clk_hw_unregister_gate(struct clk_hw *hw) +{ + struct clk_gate *gate; + + gate = to_clk_gate(hw); + + clk_hw_unregister(hw); + kfree(gate); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_gate); diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 08f65acc5d57..86b245746a6b 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -94,13 +94,13 @@ const struct clk_ops clk_gpio_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_gpio_mux_ops); -static struct clk *clk_register_gpio(struct device *dev, const char *name, +static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned gpio, bool active_low, unsigned long flags, const struct clk_ops *clk_gpio_ops) { struct clk_gpio *clk_gpio; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init = {}; unsigned long gpio_flags; int err; @@ -141,24 +141,26 @@ static struct clk *clk_register_gpio(struct device *dev, const char *name, clk_gpio->gpiod = gpio_to_desc(gpio); clk_gpio->hw.init = &init; + hw = &clk_gpio->hw; if (dev) - clk = devm_clk_register(dev, &clk_gpio->hw); + err = devm_clk_hw_register(dev, hw); else - clk = clk_register(NULL, &clk_gpio->hw); + err = clk_hw_register(NULL, hw); - if (!IS_ERR(clk)) - return clk; + if (!err) + return hw; if (!dev) { gpiod_put(clk_gpio->gpiod); kfree(clk_gpio); } - return clk; + return ERR_PTR(err); } /** - * clk_register_gpio_gate - register a gpio clock gate with the clock framework + * clk_hw_register_gpio_gate - register a gpio clock gate with the clock + * framework * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of this clock's parent @@ -166,7 +168,7 @@ static struct clk *clk_register_gpio(struct device *dev, const char *name, * @active_low: true if gpio should be set to 0 to enable clock * @flags: clock flags */ -struct clk *clk_register_gpio_gate(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, const char *parent_name, unsigned gpio, bool active_low, unsigned long flags) { @@ -175,10 +177,24 @@ struct clk *clk_register_gpio_gate(struct device *dev, const char *name, (parent_name ? 1 : 0), gpio, active_low, flags, &clk_gpio_gate_ops); } +EXPORT_SYMBOL_GPL(clk_hw_register_gpio_gate); + +struct clk *clk_register_gpio_gate(struct device *dev, const char *name, + const char *parent_name, unsigned gpio, bool active_low, + unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_gpio_gate(dev, name, parent_name, gpio, active_low, + flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; +} EXPORT_SYMBOL_GPL(clk_register_gpio_gate); /** - * clk_register_gpio_mux - register a gpio clock mux with the clock framework + * clk_hw_register_gpio_mux - register a gpio clock mux with the clock framework * @dev: device that is registering this clock * @name: name of this clock * @parent_names: names of this clock's parents @@ -187,7 +203,7 @@ EXPORT_SYMBOL_GPL(clk_register_gpio_gate); * @active_low: true if gpio should be set to 0 to enable clock * @flags: clock flags */ -struct clk *clk_register_gpio_mux(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned gpio, bool active_low, unsigned long flags) { @@ -199,6 +215,20 @@ struct clk *clk_register_gpio_mux(struct device *dev, const char *name, return clk_register_gpio(dev, name, parent_names, num_parents, gpio, active_low, flags, &clk_gpio_mux_ops); } +EXPORT_SYMBOL_GPL(clk_hw_register_gpio_mux); + +struct clk *clk_register_gpio_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, unsigned gpio, + bool active_low, unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_gpio_mux(dev, name, parent_names, num_parents, + gpio, active_low, flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; +} EXPORT_SYMBOL_GPL(clk_register_gpio_mux); static int gpio_clk_driver_probe(struct platform_device *pdev) diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c index d4c61985f448..5097831387ff 100644 --- a/drivers/clk/clk-ls1x.c +++ b/drivers/clk/clk-ls1x.c @@ -88,8 +88,7 @@ void __init ls1x_clk_init(void) { struct clk *clk; - clk = clk_register_fixed_rate(NULL, "osc_33m_clk", NULL, CLK_IS_ROOT, - OSC); + clk = clk_register_fixed_rate(NULL, "osc_33m_clk", NULL, 0, OSC); clk_register_clkdev(clk, "osc_33m_clk", NULL); /* clock derived from 33 MHz OSC clk */ diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 252188fd8bcd..16a3d5717f4e 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -113,16 +113,17 @@ const struct clk_ops clk_mux_ro_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ro_ops); -struct clk *clk_register_mux_table(struct device *dev, const char *name, +struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; u8 width = 0; + int ret; if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { width = fls(mask) - ffs(mask) + 1; @@ -157,12 +158,31 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, mux->table = table; mux->hw.init = &init; - clk = clk_register(dev, &mux->hw); - - if (IS_ERR(clk)) + hw = &mux->hw; + ret = clk_hw_register(dev, hw); + if (ret) { kfree(mux); + hw = ERR_PTR(ret); + } - return clk; + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_mux_table); + +struct clk *clk_register_mux_table(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) +{ + struct clk_hw *hw; + + hw = clk_hw_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + table, lock); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; } EXPORT_SYMBOL_GPL(clk_register_mux_table); @@ -180,6 +200,20 @@ struct clk *clk_register_mux(struct device *dev, const char *name, } EXPORT_SYMBOL_GPL(clk_register_mux); +struct clk_hw *clk_hw_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_hw_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} +EXPORT_SYMBOL_GPL(clk_hw_register_mux); + void clk_unregister_mux(struct clk *clk) { struct clk_mux *mux; @@ -195,3 +229,14 @@ void clk_unregister_mux(struct clk *clk) kfree(mux); } EXPORT_SYMBOL_GPL(clk_unregister_mux); + +void clk_hw_unregister_mux(struct clk_hw *hw) +{ + struct clk_mux *mux; + + mux = to_clk_mux(hw); + + clk_hw_unregister(hw); + kfree(mux); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_mux); diff --git a/drivers/clk/clk-nspire.c b/drivers/clk/clk-nspire.c index a378db7b2382..64f196a90816 100644 --- a/drivers/clk/clk-nspire.c +++ b/drivers/clk/clk-nspire.c @@ -125,8 +125,7 @@ static void __init nspire_clk_setup(struct device_node *node, of_property_read_string(node, "clock-output-names", &clk_name); - clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, - info.base_clock); + clk = clk_register_fixed_rate(NULL, clk_name, NULL, 0, info.base_clock); if (!IS_ERR(clk)) of_clk_add_provider(node, of_clk_src_simple_get, clk); else diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c new file mode 100644 index 000000000000..efba7d4dbcfc --- /dev/null +++ b/drivers/clk/clk-oxnas.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2010 Broadcom + * Copyright (C) 2012 Stephen Warren + * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/stringify.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +/* Standard regmap gate clocks */ +struct clk_oxnas { + struct clk_hw hw; + signed char bit; + struct regmap *regmap; +}; + +/* Regmap offsets */ +#define CLK_STAT_REGOFFSET 0x24 +#define CLK_SET_REGOFFSET 0x2c +#define CLK_CLR_REGOFFSET 0x30 + +static inline struct clk_oxnas *to_clk_oxnas(struct clk_hw *hw) +{ + return container_of(hw, struct clk_oxnas, hw); +} + +static int oxnas_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_oxnas *std = to_clk_oxnas(hw); + int ret; + unsigned int val; + + ret = regmap_read(std->regmap, CLK_STAT_REGOFFSET, &val); + if (ret < 0) + return ret; + + return val & BIT(std->bit); +} + +static int oxnas_clk_enable(struct clk_hw *hw) +{ + struct clk_oxnas *std = to_clk_oxnas(hw); + + regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit)); + + return 0; +} + +static void oxnas_clk_disable(struct clk_hw *hw) +{ + struct clk_oxnas *std = to_clk_oxnas(hw); + + regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit)); +} + +static const struct clk_ops oxnas_clk_ops = { + .enable = oxnas_clk_enable, + .disable = oxnas_clk_disable, + .is_enabled = oxnas_clk_is_enabled, +}; + +static const char *const oxnas_clk_parents[] = { + "oscillator", +}; + +static const char *const eth_parents[] = { + "gmacclk", +}; + +#define DECLARE_STD_CLKP(__clk, __parent) \ +static const struct clk_init_data clk_##__clk##_init = { \ + .name = __stringify(__clk), \ + .ops = &oxnas_clk_ops, \ + .parent_names = __parent, \ + .num_parents = ARRAY_SIZE(__parent), \ +} + +#define DECLARE_STD_CLK(__clk) DECLARE_STD_CLKP(__clk, oxnas_clk_parents) + +/* Hardware Bit - Clock association */ +struct clk_oxnas_init_data { + unsigned long bit; + const struct clk_init_data *clk_init; +}; + +/* Clk init data declaration */ +DECLARE_STD_CLK(leon); +DECLARE_STD_CLK(dma_sgdma); +DECLARE_STD_CLK(cipher); +DECLARE_STD_CLK(sata); +DECLARE_STD_CLK(audio); +DECLARE_STD_CLK(usbmph); +DECLARE_STD_CLKP(etha, eth_parents); +DECLARE_STD_CLK(pciea); +DECLARE_STD_CLK(nand); + +/* Table index is clock indice */ +static const struct clk_oxnas_init_data clk_oxnas_init[] = { + [0] = {0, &clk_leon_init}, + [1] = {1, &clk_dma_sgdma_init}, + [2] = {2, &clk_cipher_init}, + /* Skip & Do not touch to DDR clock */ + [3] = {4, &clk_sata_init}, + [4] = {5, &clk_audio_init}, + [5] = {6, &clk_usbmph_init}, + [6] = {7, &clk_etha_init}, + [7] = {8, &clk_pciea_init}, + [8] = {9, &clk_nand_init}, +}; + +struct clk_oxnas_data { + struct clk_oxnas clk_oxnas[ARRAY_SIZE(clk_oxnas_init)]; + struct clk_onecell_data onecell_data[ARRAY_SIZE(clk_oxnas_init)]; + struct clk *clks[ARRAY_SIZE(clk_oxnas_init)]; +}; + +static int oxnas_stdclk_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct clk_oxnas_data *clk_oxnas; + struct regmap *regmap; + int i; + + clk_oxnas = devm_kzalloc(&pdev->dev, sizeof(*clk_oxnas), GFP_KERNEL); + if (!clk_oxnas) + return -ENOMEM; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (!regmap) { + dev_err(&pdev->dev, "failed to have parent regmap\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(clk_oxnas_init); i++) { + struct clk_oxnas *_clk; + + _clk = &clk_oxnas->clk_oxnas[i]; + _clk->bit = clk_oxnas_init[i].bit; + _clk->hw.init = clk_oxnas_init[i].clk_init; + _clk->regmap = regmap; + + clk_oxnas->clks[i] = + devm_clk_register(&pdev->dev, &_clk->hw); + if (WARN_ON(IS_ERR(clk_oxnas->clks[i]))) + return PTR_ERR(clk_oxnas->clks[i]); + } + + clk_oxnas->onecell_data->clks = clk_oxnas->clks; + clk_oxnas->onecell_data->clk_num = ARRAY_SIZE(clk_oxnas_init); + + return of_clk_add_provider(np, of_clk_src_onecell_get, + clk_oxnas->onecell_data); +} + +static int oxnas_stdclk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id oxnas_stdclk_dt_ids[] = { + { .compatible = "oxsemi,ox810se-stdclk" }, + { } +}; +MODULE_DEVICE_TABLE(of, oxnas_stdclk_dt_ids); + +static struct platform_driver oxnas_stdclk_driver = { + .probe = oxnas_stdclk_probe, + .remove = oxnas_stdclk_remove, + .driver = { + .name = "oxnas-stdclk", + .of_match_table = oxnas_stdclk_dt_ids, + }, +}; + +module_platform_driver(oxnas_stdclk_driver); diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 9c0b8e6b1ab3..8328863cb0e0 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -132,7 +132,7 @@ static const struct palmas_clks_of_match_data palmas_of_clk32kg = { .init = { .name = "clk32kg", .ops = &palmas_clks_ops, - .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, + .flags = CLK_IGNORE_UNUSED, }, .desc = { .clk_name = "clk32kg", @@ -148,7 +148,7 @@ static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = { .init = { .name = "clk32kgaudio", .ops = &palmas_clks_ops, - .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, + .flags = CLK_IGNORE_UNUSED, }, .desc = { .clk_name = "clk32kgaudio", diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 7bc1c4527ae4..58566a17944a 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -869,14 +869,15 @@ static void __init core_mux_init(struct device_node *np) } } -static struct clk *sysclk_from_fixed(struct device_node *node, const char *name) +static struct clk __init +*sysclk_from_fixed(struct device_node *node, const char *name) { u32 rate; if (of_property_read_u32(node, "clock-frequency", &rate)) return ERR_PTR(-ENODEV); - return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); + return clk_register_fixed_rate(NULL, name, NULL, 0, rate); } static struct clk *sysclk_from_parent(const char *name) diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c index 0fee2f4ca258..74383039761e 100644 --- a/drivers/clk/clk-rk808.c +++ b/drivers/clk/clk-rk808.c @@ -106,7 +106,6 @@ static int rk808_clkout_probe(struct platform_device *pdev) if (!clk_table) return -ENOMEM; - init.flags = CLK_IS_ROOT; init.parent_names = NULL; init.num_parents = 0; init.name = "rk808-clkout1"; diff --git a/drivers/clk/clk-tango4.c b/drivers/clk/clk-tango4.c index 004ab7dfcfe3..eef75e305a59 100644 --- a/drivers/clk/clk-tango4.c +++ b/drivers/clk/clk-tango4.c @@ -4,17 +4,19 @@ #include <linux/init.h> #include <linux/io.h> -static struct clk *out[2]; -static struct clk_onecell_data clk_data = { out, 2 }; +#define CLK_COUNT 4 /* cpu_clk, sys_clk, usb_clk, sdio_clk */ +static struct clk *clks[CLK_COUNT]; +static struct clk_onecell_data clk_data = { clks, CLK_COUNT }; -#define SYSCLK_CTRL 0x20 -#define CPUCLK_CTRL 0x24 -#define LEGACY_DIV 0x3c +#define SYSCLK_DIV 0x20 +#define CPUCLK_DIV 0x24 +#define DIV_BYPASS BIT(23) -#define PLL_N(val) (((val) >> 0) & 0x7f) -#define PLL_K(val) (((val) >> 13) & 0x7) -#define PLL_M(val) (((val) >> 16) & 0x7) -#define DIV_INDEX(val) (((val) >> 8) & 0xf) +/*** CLKGEN_PLL ***/ +#define extract_pll_n(val) ((val >> 0) & ((1u << 7) - 1)) +#define extract_pll_k(val) ((val >> 13) & ((1u << 3) - 1)) +#define extract_pll_m(val) ((val >> 16) & ((1u << 3) - 1)) +#define extract_pll_isel(val) ((val >> 24) & ((1u << 3) - 1)) static void __init make_pll(int idx, const char *parent, void __iomem *base) { @@ -22,40 +24,61 @@ static void __init make_pll(int idx, const char *parent, void __iomem *base) u32 val, mul, div; sprintf(name, "pll%d", idx); - val = readl_relaxed(base + idx*8); - mul = PLL_N(val) + 1; - div = (PLL_M(val) + 1) << PLL_K(val); + val = readl(base + idx * 8); + mul = extract_pll_n(val) + 1; + div = (extract_pll_m(val) + 1) << extract_pll_k(val); clk_register_fixed_factor(NULL, name, parent, 0, mul, div); + if (extract_pll_isel(val) != 1) + panic("%s: input not set to XTAL_IN\n", name); } -static int __init get_div(void __iomem *base) +static void __init make_cd(int idx, void __iomem *base) { - u8 sysclk_tab[16] = { 2, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4 }; - int idx = DIV_INDEX(readl_relaxed(base + LEGACY_DIV)); + char name[8]; + u32 val, mul, div; - return sysclk_tab[idx]; + sprintf(name, "cd%d", idx); + val = readl(base + idx * 8); + mul = 1 << 27; + div = (2 << 27) + val; + clk_register_fixed_factor(NULL, name, "pll2", 0, mul, div); + if (val > 0xf0000000) + panic("%s: unsupported divider %x\n", name, val); } static void __init tango4_clkgen_setup(struct device_node *np) { - int div, ret; + struct clk **pp = clk_data.clks; void __iomem *base = of_iomap(np, 0); const char *parent = of_clk_get_parent_name(np, 0); if (!base) - panic("%s: invalid address\n", np->full_name); + panic("%s: invalid address\n", np->name); + + if (readl(base + CPUCLK_DIV) & DIV_BYPASS) + panic("%s: unsupported cpuclk setup\n", np->name); + + if (readl(base + SYSCLK_DIV) & DIV_BYPASS) + panic("%s: unsupported sysclk setup\n", np->name); + + writel(0x100, base + CPUCLK_DIV); /* disable frequency ramping */ make_pll(0, parent, base); make_pll(1, parent, base); + make_pll(2, parent, base); + make_cd(2, base + 0x80); + make_cd(6, base + 0x80); - out[0] = clk_register_divider(NULL, "cpuclk", "pll0", 0, - base + CPUCLK_CTRL, 8, 8, CLK_DIVIDER_ONE_BASED, NULL); + pp[0] = clk_register_divider(NULL, "cpu_clk", "pll0", 0, + base + CPUCLK_DIV, 8, 8, CLK_DIVIDER_ONE_BASED, NULL); + pp[1] = clk_register_fixed_factor(NULL, "sys_clk", "pll1", 0, 1, 4); + pp[2] = clk_register_fixed_factor(NULL, "usb_clk", "cd2", 0, 1, 2); + pp[3] = clk_register_fixed_factor(NULL, "sdio_clk", "cd6", 0, 1, 2); - div = readl_relaxed(base + SYSCLK_CTRL) & BIT(23) ? get_div(base) : 4; - out[1] = clk_register_fixed_factor(NULL, "sysclk", "pll1", 0, 1, div); + if (IS_ERR(pp[0]) || IS_ERR(pp[1]) || IS_ERR(pp[2]) || IS_ERR(pp[3])) + panic("%s: clk registration failed\n", np->name); - ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - if (IS_ERR(out[0]) || IS_ERR(out[1]) || ret < 0) - panic("%s: clk registration failed\n", np->full_name); + if (of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data)) + panic("%s: clk provider registration failed\n", np->name); } CLK_OF_DECLARE(tango4_clkgen, "sigma,tango4-clkgen", tango4_clkgen_setup); diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 8e5ed649a098..697c66757400 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -74,7 +74,6 @@ static const struct clk_ops twl6040_mcpdm_ops = { static struct clk_init_data wm831x_clkout_init = { .name = "mcpdm_fclk", .ops = &twl6040_mcpdm_ops, - .flags = CLK_IS_ROOT, }; static int twl6040_clk_probe(struct platform_device *pdev) diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 43f9d15255f4..88def4b2761c 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -58,7 +58,6 @@ static const struct clk_ops wm831x_xtal_ops = { static struct clk_init_data wm831x_xtal_init = { .name = "xtal", .ops = &wm831x_xtal_ops, - .flags = CLK_IS_ROOT, }; static const unsigned long wm831x_fll_auto_rates[] = { diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index d73450b60b28..343313250c58 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -198,7 +198,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty of_property_read_string(np, "clock-output-names", &clk_name); clk = xgene_register_clk_pll(NULL, clk_name, of_clk_get_parent_name(np, 0), - CLK_IS_ROOT, reg, 0, pll_type, &clk_lock, + 0, reg, 0, pll_type, &clk_lock, version); if (!IS_ERR(clk)) { of_clk_add_provider(np, of_clk_src_simple_get, clk); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fb74dc1f7520..d584004f7af7 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -574,6 +574,9 @@ static void clk_core_unprepare(struct clk_core *core) if (WARN_ON(core->prepare_count == 0)) return; + if (WARN_ON(core->prepare_count == 1 && core->flags & CLK_IS_CRITICAL)) + return; + if (--core->prepare_count > 0) return; @@ -679,6 +682,9 @@ static void clk_core_disable(struct clk_core *core) if (WARN_ON(core->enable_count == 0)) return; + if (WARN_ON(core->enable_count == 1 && core->flags & CLK_IS_CRITICAL)) + return; + if (--core->enable_count > 0) return; @@ -2397,6 +2403,16 @@ static int __clk_core_init(struct clk_core *core) if (core->ops->init) core->ops->init(core->hw); + if (core->flags & CLK_IS_CRITICAL) { + unsigned long flags; + + clk_core_prepare(core); + + flags = clk_enable_lock(); + clk_core_enable(core); + clk_enable_unlock(flags); + } + kref_init(&core->ref); out: clk_prepare_unlock(); @@ -2536,6 +2552,22 @@ fail_out: } EXPORT_SYMBOL_GPL(clk_register); +/** + * clk_hw_register - register a clk_hw and return an error code + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * clk_hw_register is the primary interface for populating the clock tree with + * new clock nodes. It returns an integer equal to zero indicating success or + * less than zero indicating failure. Drivers must test for an error code after + * calling clk_hw_register(). + */ +int clk_hw_register(struct device *dev, struct clk_hw *hw) +{ + return PTR_ERR_OR_ZERO(clk_register(dev, hw)); +} +EXPORT_SYMBOL_GPL(clk_hw_register); + /* Free memory allocated for a clock. */ static void __clk_release(struct kref *ref) { @@ -2637,11 +2669,26 @@ unlock: } EXPORT_SYMBOL_GPL(clk_unregister); +/** + * clk_hw_unregister - unregister a currently registered clk_hw + * @hw: hardware-specific clock data to unregister + */ +void clk_hw_unregister(struct clk_hw *hw) +{ + clk_unregister(hw->clk); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister); + static void devm_clk_release(struct device *dev, void *res) { clk_unregister(*(struct clk **)res); } +static void devm_clk_hw_release(struct device *dev, void *res) +{ + clk_hw_unregister(*(struct clk_hw **)res); +} + /** * devm_clk_register - resource managed clk_register() * @dev: device that is registering this clock @@ -2672,6 +2719,36 @@ struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw) } EXPORT_SYMBOL_GPL(devm_clk_register); +/** + * devm_clk_hw_register - resource managed clk_hw_register() + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * Managed clk_hw_register(). Clocks registered by this function are + * automatically clk_hw_unregister()ed on driver detach. See clk_hw_register() + * for more information. + */ +int devm_clk_hw_register(struct device *dev, struct clk_hw *hw) +{ + struct clk_hw **hwp; + int ret; + + hwp = devres_alloc(devm_clk_hw_release, sizeof(*hwp), GFP_KERNEL); + if (!hwp) + return -ENOMEM; + + ret = clk_hw_register(dev, hw); + if (!ret) { + *hwp = hw; + devres_add(dev, hwp); + } else { + devres_free(hwp); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register); + static int devm_clk_match(struct device *dev, void *res, void *data) { struct clk *c = res; @@ -2680,6 +2757,15 @@ static int devm_clk_match(struct device *dev, void *res, void *data) return c == data; } +static int devm_clk_hw_match(struct device *dev, void *res, void *data) +{ + struct clk_hw *hw = res; + + if (WARN_ON(!hw)) + return 0; + return hw == data; +} + /** * devm_clk_unregister - resource managed clk_unregister() * @clk: clock to unregister @@ -2694,6 +2780,22 @@ void devm_clk_unregister(struct device *dev, struct clk *clk) } EXPORT_SYMBOL_GPL(devm_clk_unregister); +/** + * devm_clk_hw_unregister - resource managed clk_hw_unregister() + * @dev: device that is unregistering the hardware-specific clock data + * @hw: link to hardware-specific clock data + * + * Unregister a clk_hw registered with devm_clk_hw_register(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw) +{ + WARN_ON(devres_release(dev, devm_clk_hw_release, devm_clk_hw_match, + hw)); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_unregister); + /* * clkdev helpers */ @@ -2855,6 +2957,7 @@ struct of_clk_provider { struct device_node *node; struct clk *(*get)(struct of_phandle_args *clkspec, void *data); + struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data); void *data; }; @@ -2871,6 +2974,12 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, } EXPORT_SYMBOL_GPL(of_clk_src_simple_get); +struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data) +{ + return data; +} +EXPORT_SYMBOL_GPL(of_clk_hw_simple_get); + struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) { struct clk_onecell_data *clk_data = data; @@ -2885,6 +2994,21 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) } EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); +struct clk_hw * +of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_hw_onecell_data *hw_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= hw_data->num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return hw_data->hws[idx]; +} +EXPORT_SYMBOL_GPL(of_clk_hw_onecell_get); + /** * of_clk_add_provider() - Register a clock provider for a node * @np: Device node pointer associated with clock provider @@ -2921,6 +3045,41 @@ int of_clk_add_provider(struct device_node *np, EXPORT_SYMBOL_GPL(of_clk_add_provider); /** + * of_clk_add_hw_provider() - Register a clock provider for a node + * @np: Device node pointer associated with clock provider + * @get: callback for decoding clk_hw + * @data: context pointer for @get callback. + */ +int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + struct of_clk_provider *cp; + int ret; + + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->node = of_node_get(np); + cp->data = data; + cp->get_hw = get; + + mutex_lock(&of_clk_mutex); + list_add(&cp->link, &of_clk_providers); + mutex_unlock(&of_clk_mutex); + pr_debug("Added clk_hw provider from %s\n", np->full_name); + + ret = of_clk_set_defaults(np, true); + if (ret < 0) + of_clk_del_provider(np); + + return ret; +} +EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); + +/** * of_clk_del_provider() - Remove a previously registered clock provider * @np: Device node pointer associated with clock provider */ @@ -2941,11 +3100,32 @@ void of_clk_del_provider(struct device_node *np) } EXPORT_SYMBOL_GPL(of_clk_del_provider); +static struct clk_hw * +__of_clk_get_hw_from_provider(struct of_clk_provider *provider, + struct of_phandle_args *clkspec) +{ + struct clk *clk; + struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); + + if (provider->get_hw) { + hw = provider->get_hw(clkspec, provider->data); + } else if (provider->get) { + clk = provider->get(clkspec, provider->data); + if (!IS_ERR(clk)) + hw = __clk_get_hw(clk); + else + hw = ERR_CAST(clk); + } + + return hw; +} + struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, const char *dev_id, const char *con_id) { struct of_clk_provider *provider; struct clk *clk = ERR_PTR(-EPROBE_DEFER); + struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); if (!clkspec) return ERR_PTR(-EINVAL); @@ -2954,10 +3134,9 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, mutex_lock(&of_clk_mutex); list_for_each_entry(provider, &of_clk_providers, link) { if (provider->node == clkspec->np) - clk = provider->get(clkspec, provider->data); - if (!IS_ERR(clk)) { - clk = __clk_create_clk(__clk_get_hw(clk), dev_id, - con_id); + hw = __of_clk_get_hw_from_provider(provider, clkspec); + if (!IS_ERR(hw)) { + clk = __clk_create_clk(hw, dev_id, con_id); if (!IS_ERR(clk) && !__clk_get(clk)) { __clk_free_clk(clk); @@ -3127,6 +3306,41 @@ static int parent_ready(struct device_node *np) } /** + * of_clk_detect_critical() - set CLK_IS_CRITICAL flag from Device Tree + * @np: Device node pointer associated with clock provider + * @index: clock index + * @flags: pointer to clk_core->flags + * + * Detects if the clock-critical property exists and, if so, sets the + * corresponding CLK_IS_CRITICAL flag. + * + * Do not use this function. It exists only for legacy Device Tree + * bindings, such as the one-clock-per-node style that are outdated. + * Those bindings typically put all clock data into .dts and the Linux + * driver has no clock data, thus making it impossible to set this flag + * correctly from the driver. Only those drivers may call + * of_clk_detect_critical from their setup functions. + * + * Return: error code or zero on success + */ +int of_clk_detect_critical(struct device_node *np, + int index, unsigned long *flags) +{ + struct property *prop; + const __be32 *cur; + uint32_t idx; + + if (!np || !flags) + return -EINVAL; + + of_property_for_each_u32(np, "clock-critical", prop, cur, idx) + if (index == idx) + *flags |= CLK_IS_CRITICAL; + + return 0; +} + +/** * of_clk_init() - Scan and init clock providers from the DT * @matches: array of compatible values and init functions for providers. * diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index eb20b941154b..89cc700fbc37 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -301,6 +301,20 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) } EXPORT_SYMBOL(clkdev_alloc); +struct clk_lookup * +clkdev_hw_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt, ...) +{ + struct clk_lookup *cl; + va_list ap; + + va_start(ap, dev_fmt); + cl = vclkdev_alloc(hw, con_id, dev_fmt, ap); + va_end(ap); + + return cl; +} +EXPORT_SYMBOL(clkdev_hw_alloc); + /** * clkdev_create - allocate and add a clkdev lookup structure * @clk: struct clk to associate with all clk_lookups @@ -324,6 +338,29 @@ struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id, } EXPORT_SYMBOL_GPL(clkdev_create); +/** + * clkdev_hw_create - allocate and add a clkdev lookup structure + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_fmt: format string describing device name + * + * Returns a clk_lookup structure, which can be later unregistered and + * freed. + */ +struct clk_lookup *clkdev_hw_create(struct clk_hw *hw, const char *con_id, + const char *dev_fmt, ...) +{ + struct clk_lookup *cl; + va_list ap; + + va_start(ap, dev_fmt); + cl = vclkdev_create(hw, con_id, dev_fmt, ap); + va_end(ap); + + return cl; +} +EXPORT_SYMBOL_GPL(clkdev_hw_create); + int clk_add_alias(const char *alias, const char *alias_dev_name, const char *con_id, struct device *dev) { @@ -404,28 +441,28 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, EXPORT_SYMBOL(clk_register_clkdev); /** - * clk_register_clkdevs - register a set of clk_lookup for a struct clk - * @clk: struct clk to associate with all clk_lookups - * @cl: array of clk_lookup structures with con_id and dev_id pre-initialized - * @num: number of clk_lookup structures to register + * clk_hw_register_clkdev - register one clock lookup for a struct clk_hw + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name * - * To make things easier for mass registration, we detect error clks - * from a previous clk_register() call, and return the error code for - * those. This is to permit this function to be called immediately - * after clk_register(). + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. */ -int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num) +int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, + const char *dev_id) { - unsigned i; - - if (IS_ERR(clk)) - return PTR_ERR(clk); + struct clk_lookup *cl; - for (i = 0; i < num; i++, cl++) { - cl->clk_hw = __clk_get_hw(clk); - __clkdev_add(cl); - } + /* + * Since dev_id can be NULL, and NULL is handled specially, we must + * pass it as either a NULL format string, or with "%s". + */ + if (dev_id) + cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); + else + cl = __clk_register_clkdev(hw, con_id, NULL); - return 0; + return cl ? 0 : -ENOMEM; } -EXPORT_SYMBOL(clk_register_clkdevs); +EXPORT_SYMBOL(clk_hw_register_clkdev); diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index e43485448612..3f537a04c6a6 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -1,3 +1,11 @@ +config COMMON_CLK_HI3519 + tristate "Hi3519 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_HISI + default ARCH_HISI + help + Build the clock driver for hi3519. + config COMMON_CLK_HI6220 bool "Hi6220 Clock Driver" depends on ARCH_HISI || COMPILE_TEST @@ -5,6 +13,13 @@ config COMMON_CLK_HI6220 help Build the Hisilicon Hi6220 clock driver based on the common clock framework. +config RESET_HISI + bool "HiSilicon Reset Controller Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_CONTROLLER + help + Build reset controller driver for HiSilicon device chipsets. + config STUB_CLK_HI6220 bool "Hi6220 Stub Clock Driver" depends on COMMON_CLK_HI6220 && MAILBOX diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 74dba31590f9..e169ec7da023 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -7,5 +7,7 @@ obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o +obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o +obj-$(CONFIG_RESET_HISI) += reset.o obj-$(CONFIG_STUB_CLK_HI6220) += clk-hi6220-stub.o diff --git a/drivers/clk/hisilicon/clk-hi3519.c b/drivers/clk/hisilicon/clk-hi3519.c new file mode 100644 index 000000000000..715c7301a66a --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3519.c @@ -0,0 +1,131 @@ +/* + * Hi3519 Clock Driver + * + * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dt-bindings/clock/hi3519-clock.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include "clk.h" +#include "reset.h" + +#define HI3519_INNER_CLK_OFFSET 64 +#define HI3519_FIXED_24M 65 +#define HI3519_FIXED_50M 66 +#define HI3519_FIXED_75M 67 +#define HI3519_FIXED_125M 68 +#define HI3519_FIXED_150M 69 +#define HI3519_FIXED_200M 70 +#define HI3519_FIXED_250M 71 +#define HI3519_FIXED_300M 72 +#define HI3519_FIXED_400M 73 +#define HI3519_FMC_MUX 74 + +#define HI3519_NR_CLKS 128 + +static const struct hisi_fixed_rate_clock hi3519_fixed_rate_clks[] = { + { HI3519_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3519_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3519_FIXED_75M, "75m", NULL, 0, 75000000, }, + { HI3519_FIXED_125M, "125m", NULL, 0, 125000000, }, + { HI3519_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HI3519_FIXED_200M, "200m", NULL, 0, 200000000, }, + { HI3519_FIXED_250M, "250m", NULL, 0, 250000000, }, + { HI3519_FIXED_300M, "300m", NULL, 0, 300000000, }, + { HI3519_FIXED_400M, "400m", NULL, 0, 400000000, }, +}; + +static const char *const fmc_mux_p[] = { + "24m", "75m", "125m", "150m", "200m", "250m", "300m", "400m", }; +static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; + +static const struct hisi_mux_clock hi3519_mux_clks[] = { + { HI3519_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), + CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, +}; + +static const struct hisi_gate_clock hi3519_gate_clks[] = { + { HI3519_FMC_CLK, "clk_fmc", "fmc_mux", + CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, + { HI3519_UART0_CLK, "clk_uart0", "24m", + CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, + { HI3519_UART1_CLK, "clk_uart1", "24m", + CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, + { HI3519_UART2_CLK, "clk_uart2", "24m", + CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, + { HI3519_UART3_CLK, "clk_uart3", "24m", + CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, + { HI3519_UART4_CLK, "clk_uart4", "24m", + CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, + { HI3519_SPI0_CLK, "clk_spi0", "50m", + CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, + { HI3519_SPI1_CLK, "clk_spi1", "50m", + CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, + { HI3519_SPI2_CLK, "clk_spi2", "50m", + CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, +}; + +static int hi3519_clk_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct hisi_clock_data *clk_data; + struct hisi_reset_controller *rstc; + + rstc = hisi_reset_init(np); + if (!rstc) + return -ENOMEM; + + clk_data = hisi_clk_init(np, HI3519_NR_CLKS); + if (!clk_data) { + hisi_reset_exit(rstc); + return -ENODEV; + } + + hisi_clk_register_fixed_rate(hi3519_fixed_rate_clks, + ARRAY_SIZE(hi3519_fixed_rate_clks), + clk_data); + hisi_clk_register_mux(hi3519_mux_clks, ARRAY_SIZE(hi3519_mux_clks), + clk_data); + hisi_clk_register_gate(hi3519_gate_clks, + ARRAY_SIZE(hi3519_gate_clks), clk_data); + + return 0; +} + +static const struct of_device_id hi3519_clk_match_table[] = { + { .compatible = "hisilicon,hi3519-crg" }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3519_clk_match_table); + +static struct platform_driver hi3519_clk_driver = { + .probe = hi3519_clk_probe, + .driver = { + .name = "hi3519-clk", + .of_match_table = hi3519_clk_match_table, + }, +}; + +static int __init hi3519_clk_init(void) +{ + return platform_driver_register(&hi3519_clk_driver); +} +core_initcall(hi3519_clk_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3519 Clock Driver"); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index 9f8e76676553..9b15adbfc30c 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -37,7 +37,7 @@ static DEFINE_SPINLOCK(hisi_clk_lock); -struct hisi_clock_data __init *hisi_clk_init(struct device_node *np, +struct hisi_clock_data *hisi_clk_init(struct device_node *np, int nr_clks) { struct hisi_clock_data *clk_data; @@ -71,8 +71,9 @@ err_data: err: return NULL; } +EXPORT_SYMBOL_GPL(hisi_clk_init); -void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks, +void hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; @@ -91,8 +92,9 @@ void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } +EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate); -void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks, +void hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks, int nums, struct hisi_clock_data *data) { @@ -112,8 +114,9 @@ void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } +EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor); -void __init hisi_clk_register_mux(struct hisi_mux_clock *clks, +void hisi_clk_register_mux(const struct hisi_mux_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; @@ -141,8 +144,9 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } +EXPORT_SYMBOL_GPL(hisi_clk_register_mux); -void __init hisi_clk_register_divider(struct hisi_divider_clock *clks, +void hisi_clk_register_divider(const struct hisi_divider_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; @@ -170,8 +174,9 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } +EXPORT_SYMBOL_GPL(hisi_clk_register_divider); -void __init hisi_clk_register_gate(struct hisi_gate_clock *clks, +void hisi_clk_register_gate(const struct hisi_gate_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; @@ -198,8 +203,9 @@ void __init hisi_clk_register_gate(struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } +EXPORT_SYMBOL_GPL(hisi_clk_register_gate); -void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, +void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; @@ -226,8 +232,9 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } +EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep); -void __init hi6220_clk_register_divider(struct hi6220_divider_clock *clks, +void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index b56fbc1c5f27..20d64afe4ad8 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -111,18 +111,18 @@ struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, u8 shift, u8 width, u32 mask_bit, spinlock_t *lock); struct hisi_clock_data *hisi_clk_init(struct device_node *, int); -void hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, +void hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *, int, struct hisi_clock_data *); -void hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, +void hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *, int, struct hisi_clock_data *); -void hisi_clk_register_mux(struct hisi_mux_clock *, int, +void hisi_clk_register_mux(const struct hisi_mux_clock *, int, struct hisi_clock_data *); -void hisi_clk_register_divider(struct hisi_divider_clock *, +void hisi_clk_register_divider(const struct hisi_divider_clock *, int, struct hisi_clock_data *); -void hisi_clk_register_gate(struct hisi_gate_clock *, +void hisi_clk_register_gate(const struct hisi_gate_clock *, int, struct hisi_clock_data *); -void hisi_clk_register_gate_sep(struct hisi_gate_clock *, +void hisi_clk_register_gate_sep(const struct hisi_gate_clock *, int, struct hisi_clock_data *); -void hi6220_clk_register_divider(struct hi6220_divider_clock *, +void hi6220_clk_register_divider(const struct hi6220_divider_clock *, int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c new file mode 100644 index 000000000000..6aa49c2204d0 --- /dev/null +++ b/drivers/clk/hisilicon/reset.c @@ -0,0 +1,134 @@ +/* + * Hisilicon Reset Controller Driver + * + * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "reset.h" + +#define HISI_RESET_BIT_MASK 0x1f +#define HISI_RESET_OFFSET_SHIFT 8 +#define HISI_RESET_OFFSET_MASK 0xffff00 + +struct hisi_reset_controller { + spinlock_t lock; + void __iomem *membase; + struct reset_controller_dev rcdev; +}; + + +#define to_hisi_reset_controller(rcdev) \ + container_of(rcdev, struct hisi_reset_controller, rcdev) + +static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + u32 offset; + u8 bit; + + offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT) + & HISI_RESET_OFFSET_MASK; + bit = reset_spec->args[1] & HISI_RESET_BIT_MASK; + + return (offset | bit); +} + +static int hisi_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); + unsigned long flags; + u32 offset, reg; + u8 bit; + + offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; + bit = id & HISI_RESET_BIT_MASK; + + spin_lock_irqsave(&rstc->lock, flags); + + reg = readl(rstc->membase + offset); + writel(reg | BIT(bit), rstc->membase + offset); + + spin_unlock_irqrestore(&rstc->lock, flags); + + return 0; +} + +static int hisi_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); + unsigned long flags; + u32 offset, reg; + u8 bit; + + offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; + bit = id & HISI_RESET_BIT_MASK; + + spin_lock_irqsave(&rstc->lock, flags); + + reg = readl(rstc->membase + offset); + writel(reg & ~BIT(bit), rstc->membase + offset); + + spin_unlock_irqrestore(&rstc->lock, flags); + + return 0; +} + +static const struct reset_control_ops hisi_reset_ops = { + .assert = hisi_reset_assert, + .deassert = hisi_reset_deassert, +}; + +struct hisi_reset_controller *hisi_reset_init(struct device_node *np) +{ + struct hisi_reset_controller *rstc; + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return NULL; + + rstc->membase = of_iomap(np, 0); + if (!rstc->membase) { + kfree(rstc); + return NULL; + } + + spin_lock_init(&rstc->lock); + + rstc->rcdev.owner = THIS_MODULE; + rstc->rcdev.ops = &hisi_reset_ops; + rstc->rcdev.of_node = np; + rstc->rcdev.of_reset_n_cells = 2; + rstc->rcdev.of_xlate = hisi_reset_of_xlate; + reset_controller_register(&rstc->rcdev); + + return rstc; +} +EXPORT_SYMBOL_GPL(hisi_reset_init); + +void hisi_reset_exit(struct hisi_reset_controller *rstc) +{ + reset_controller_unregister(&rstc->rcdev); + iounmap(rstc->membase); + kfree(rstc); +} +EXPORT_SYMBOL_GPL(hisi_reset_exit); diff --git a/drivers/clk/hisilicon/reset.h b/drivers/clk/hisilicon/reset.h new file mode 100644 index 000000000000..677d773ed27c --- /dev/null +++ b/drivers/clk/hisilicon/reset.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __HISI_RESET_H +#define __HISI_RESET_H + +struct device_node; +struct hisi_reset_controller; + +#ifdef CONFIG_RESET_CONTROLLER +struct hisi_reset_controller *hisi_reset_init(struct device_node *np); +void hisi_reset_exit(struct hisi_reset_controller *rstc); +#else +static inline hisi_reset_controller *hisi_reset_init(struct device_node *np) +{ + return 0; +} +static inline void hisi_reset_exit(struct hisi_reset_controller *rstc) +{} +#endif + +#endif /* __HISI_RESET_H */ diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index 8935bff99fe7..db44a198a0d9 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -31,6 +31,7 @@ struct clk_gate2 { struct clk_hw hw; void __iomem *reg; u8 bit_idx; + u8 cgr_val; u8 flags; spinlock_t *lock; unsigned int *share_count; @@ -50,7 +51,8 @@ static int clk_gate2_enable(struct clk_hw *hw) goto out; reg = readl(gate->reg); - reg |= 3 << gate->bit_idx; + reg &= ~(3 << gate->bit_idx); + reg |= gate->cgr_val << gate->bit_idx; writel(reg, gate->reg); out: @@ -125,7 +127,7 @@ static struct clk_ops clk_gate2_ops = { struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, - void __iomem *reg, u8 bit_idx, + void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate2_flags, spinlock_t *lock, unsigned int *share_count) { @@ -140,6 +142,7 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, /* struct clk_gate2 assignments */ gate->reg = reg; gate->bit_idx = bit_idx; + gate->cgr_val = cgr_val; gate->flags = clk_gate2_flags; gate->lock = lock; gate->share_count = share_count; diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index a71d24cb4c06..b0978d3b83e2 100644 --- a/drivers/clk/imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c @@ -66,7 +66,7 @@ static const char *std_sel[] = {"ppll", "arm"}; static const char *ipg_per_sel[] = {"ahb_per_div", "arm_per_div"}; enum mx35_clks { - ckih, ckil, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg, + ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg, arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, esdhc_sel, esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, spdif_div_pre, spdif_div_post, ssi_sel, ssi1_div_pre, ssi1_div_post, ssi2_div_pre, @@ -79,7 +79,7 @@ enum mx35_clks { rtc_gate, rtic_gate, scc_gate, sdma_gate, spba_gate, spdif_gate, ssi1_gate, ssi2_gate, uart1_gate, uart2_gate, uart3_gate, usbotg_gate, wdog_gate, max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate, - gpu2d_gate, clk_max + gpu2d_gate, ckil, clk_max }; static struct clk *clk[clk_max]; diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index fea125eb4330..97e742a8be17 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -134,6 +134,8 @@ static u32 share_count_esai; static u32 share_count_ssi1; static u32 share_count_ssi2; static u32 share_count_ssi3; +static u32 share_count_sai1; +static u32 share_count_sai2; static struct clk ** const uart_clks[] __initconst = { &clks[IMX6SX_CLK_UART_IPG], @@ -469,10 +471,10 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_SSI3] = imx_clk_gate2_shared("ssi3", "ssi3_podf", base + 0x7c, 22, &share_count_ssi3); clks[IMX6SX_CLK_UART_IPG] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); clks[IMX6SX_CLK_UART_SERIAL] = imx_clk_gate2("uart_serial", "uart_podf", base + 0x7c, 26); - clks[IMX6SX_CLK_SAI1_IPG] = imx_clk_gate2("sai1_ipg", "ipg", base + 0x7c, 28); - clks[IMX6SX_CLK_SAI2_IPG] = imx_clk_gate2("sai2_ipg", "ipg", base + 0x7c, 30); - clks[IMX6SX_CLK_SAI1] = imx_clk_gate2("sai1", "ssi1_podf", base + 0x7c, 28); - clks[IMX6SX_CLK_SAI2] = imx_clk_gate2("sai2", "ssi2_podf", base + 0x7c, 30); + clks[IMX6SX_CLK_SAI1_IPG] = imx_clk_gate2_shared("sai1_ipg", "ipg", base + 0x7c, 28, &share_count_sai1); + clks[IMX6SX_CLK_SAI2_IPG] = imx_clk_gate2_shared("sai2_ipg", "ipg", base + 0x7c, 30, &share_count_sai2); + clks[IMX6SX_CLK_SAI1] = imx_clk_gate2_shared("sai1", "ssi1_podf", base + 0x7c, 28, &share_count_sai1); + clks[IMX6SX_CLK_SAI2] = imx_clk_gate2_shared("sai2", "ssi2_podf", base + 0x7c, 30, &share_count_sai2); /* CCGR6 */ clks[IMX6SX_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index fbb6a8c8653d..522996800d5b 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -56,7 +56,7 @@ static const char *nand_usdhc_bus_sel[] = { "osc", "pll_sys_pfd2_270m_clk", "pll_sys_pfd2_135m_clk", "pll_sys_pfd6_clk", "pll_enet_250m_clk", "pll_audio_main_clk", }; -static const char *ahb_channel_sel[] = { "osc", "pll_sys_pfd2_135m_clk", +static const char *ahb_channel_sel[] = { "osc", "pll_sys_pfd2_270m_clk", "pll_dram_533m_clk", "pll_sys_pfd0_392m_clk", "pll_enet_125m_clk", "pll_usb_main_clk", "pll_audio_main_clk", "pll_video_main_clk", }; @@ -342,7 +342,7 @@ static const char *clko1_sel[] = { "osc", "pll_sys_main_clk", static const char *clko2_sel[] = { "osc", "pll_sys_main_240m_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_166m_clk", "pll_sys_pfd4_clk", - "pll_audio_main_clk", "pll_video_main_clk", "osc_32k_clk", }; + "pll_audio_main_clk", "pll_video_main_clk", "ckil", }; static const char *lvds1_sel[] = { "pll_arm_main_clk", "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_332m_clk", @@ -382,6 +382,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_CLK_DUMMY] = imx_clk_fixed("dummy", 0); clks[IMX7D_OSC_24M_CLK] = of_clk_get_by_name(ccm_node, "osc"); + clks[IMX7D_CKIL] = of_clk_get_by_name(ccm_node, "ckil"); np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop"); base = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index c05c43d56a94..4826b3c9e19e 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -44,6 +44,7 @@ struct clk_pllv3 { u32 powerdown; u32 div_mask; u32 div_shift; + unsigned long ref_clock; }; #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw) @@ -286,7 +287,9 @@ static const struct clk_ops clk_pllv3_av_ops = { static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - return 500000000; + struct clk_pllv3 *pll = to_clk_pllv3(hw); + + return pll->ref_clock; } static const struct clk_ops clk_pllv3_enet_ops = { @@ -326,7 +329,11 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, break; case IMX_PLLV3_ENET_IMX7: pll->powerdown = IMX7_ENET_PLL_POWER; + pll->ref_clock = 1000000000; + ops = &clk_pllv3_enet_ops; + break; case IMX_PLLV3_ENET: + pll->ref_clock = 500000000; ops = &clk_pllv3_enet_ops; break; default: diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 0a94d9661d91..3a1f24475ee4 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -10,6 +10,7 @@ #include <linux/of_address.h> #include <linux/clk.h> +#include <linux/syscore_ops.h> #include <dt-bindings/clock/vf610-clock.h> #include "clk.h" @@ -40,6 +41,7 @@ #define CCM_CCGR9 (ccm_base + 0x64) #define CCM_CCGR10 (ccm_base + 0x68) #define CCM_CCGR11 (ccm_base + 0x6c) +#define CCM_CCGRx(x) (CCM_CCGR0 + (x) * 4) #define CCM_CMEOR0 (ccm_base + 0x70) #define CCM_CMEOR1 (ccm_base + 0x74) #define CCM_CMEOR2 (ccm_base + 0x78) @@ -115,10 +117,19 @@ static struct clk_div_table pll4_audio_div_table[] = { static struct clk *clk[VF610_CLK_END]; static struct clk_onecell_data clk_data; +static u32 cscmr1; +static u32 cscmr2; +static u32 cscdr1; +static u32 cscdr2; +static u32 cscdr3; +static u32 ccgr[12]; + static unsigned int const clks_init_on[] __initconst = { VF610_CLK_SYS_BUS, VF610_CLK_DDR_SEL, VF610_CLK_DAP, + VF610_CLK_DDRMC, + VF610_CLK_WKPU, }; static struct clk * __init vf610_get_fixed_clock( @@ -132,6 +143,43 @@ static struct clk * __init vf610_get_fixed_clock( return clk; }; +static int vf610_clk_suspend(void) +{ + int i; + + cscmr1 = readl_relaxed(CCM_CSCMR1); + cscmr2 = readl_relaxed(CCM_CSCMR2); + + cscdr1 = readl_relaxed(CCM_CSCDR1); + cscdr2 = readl_relaxed(CCM_CSCDR2); + cscdr3 = readl_relaxed(CCM_CSCDR3); + + for (i = 0; i < 12; i++) + ccgr[i] = readl_relaxed(CCM_CCGRx(i)); + + return 0; +} + +static void vf610_clk_resume(void) +{ + int i; + + writel_relaxed(cscmr1, CCM_CSCMR1); + writel_relaxed(cscmr2, CCM_CSCMR2); + + writel_relaxed(cscdr1, CCM_CSCDR1); + writel_relaxed(cscdr2, CCM_CSCDR2); + writel_relaxed(cscdr3, CCM_CSCDR3); + + for (i = 0; i < 12; i++) + writel_relaxed(ccgr[i], CCM_CCGRx(i)); +} + +static struct syscore_ops vf610_clk_syscore_ops = { + .suspend = vf610_clk_suspend, + .resume = vf610_clk_resume, +}; + static void __init vf610_clocks_init(struct device_node *ccm_node) { struct device_node *np; @@ -233,6 +281,9 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, 0, pll4_audio_div_table, &imx_ccm_lock); clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1); + clk[VF610_CLK_DDRMC] = imx_clk_gate2_cgr("ddrmc", "ddr_sel", CCM_CCGR6, CCM_CCGRx_CGn(14), 0x2); + clk[VF610_CLK_WKPU] = imx_clk_gate2_cgr("wkpu", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(10), 0x2); + clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6); clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6); @@ -321,11 +372,14 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_DCU0_SEL] = imx_clk_mux("dcu0_sel", CCM_CSCMR1, 28, 1, dcu_sels, 2); clk[VF610_CLK_DCU0_EN] = imx_clk_gate("dcu0_en", "dcu0_sel", CCM_CSCDR3, 19); clk[VF610_CLK_DCU0_DIV] = imx_clk_divider("dcu0_div", "dcu0_en", CCM_CSCDR3, 16, 3); - clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "dcu0_div", CCM_CCGR3, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "ipg_bus", CCM_CCGR3, CCM_CCGRx_CGn(8)); clk[VF610_CLK_DCU1_SEL] = imx_clk_mux("dcu1_sel", CCM_CSCMR1, 29, 1, dcu_sels, 2); clk[VF610_CLK_DCU1_EN] = imx_clk_gate("dcu1_en", "dcu1_sel", CCM_CSCDR3, 23); clk[VF610_CLK_DCU1_DIV] = imx_clk_divider("dcu1_div", "dcu1_en", CCM_CSCDR3, 20, 3); - clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "dcu1_div", CCM_CCGR9, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(8)); + + clk[VF610_CLK_TCON0] = imx_clk_gate2("tcon0", "platform_bus", CCM_CCGR1, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_TCON1] = imx_clk_gate2("tcon1", "platform_bus", CCM_CCGR7, CCM_CCGRx_CGn(13)); clk[VF610_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", CCM_CSCMR1, 20, 2, esai_sels, 4); clk[VF610_CLK_ESAI_EN] = imx_clk_gate("esai_en", "esai_sel", CCM_CSCDR2, 30); @@ -409,6 +463,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clk[clks_init_on[i]]); + register_syscore_ops(&vf610_clk_syscore_ops); + /* Add the clocks to provider list */ clk_data.clks = clk; clk_data.clk_num = ARRAY_SIZE(clk); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index d942f5748d08..508d0fad84cf 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -41,7 +41,7 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, - void __iomem *reg, u8 bit_idx, + void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate_flags, spinlock_t *lock, unsigned int *share_count); @@ -55,7 +55,7 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock, NULL); + shift, 0x3, 0, &imx_ccm_lock, NULL); } static inline struct clk *imx_clk_gate2_shared(const char *name, @@ -63,7 +63,14 @@ static inline struct clk *imx_clk_gate2_shared(const char *name, unsigned int *share_count) { return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock, share_count); + shift, 0x3, 0, &imx_ccm_lock, share_count); +} + +static inline struct clk *imx_clk_gate2_cgr(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 cgr_val) +{ + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, cgr_val, 0, &imx_ccm_lock, NULL); } struct clk *imx_clk_pfd(const char *name, const char *parent_name, diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index 7cfb7b2a2ed6..e8248f9185f7 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -325,6 +325,7 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) div = (div_reg >> clk_info->div.shift) & GENMASK(clk_info->div.bits - 1, 0); div += 1; + div *= clk_info->div.div; rate /= div; } @@ -345,6 +346,14 @@ ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, div = min_t(unsigned, div, 1 << clk_info->div.bits); div = max_t(unsigned, div, 1); + /* + * If the divider value itself must be divided before being written to + * the divider register, we must ensure we don't have any bits set that + * would be lost as a result of doing so. + */ + div /= clk_info->div.div; + div *= clk_info->div.div; + return div; } @@ -395,7 +404,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, /* update the divide */ mask = GENMASK(clk_info->div.bits - 1, 0); reg &= ~(mask << clk_info->div.shift); - reg |= (div - 1) << clk_info->div.shift; + reg |= ((div / clk_info->div.div) - 1) << clk_info->div.shift; /* clear the stop bit */ if (clk_info->div.stop_bit != -1) diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h index 99347e2b97e8..09700b2c555d 100644 --- a/drivers/clk/ingenic/cgu.h +++ b/drivers/clk/ingenic/cgu.h @@ -76,8 +76,11 @@ struct ingenic_cgu_mux_info { /** * struct ingenic_cgu_div_info - information about a divider * @reg: offset of the divider control register within the CGU - * @shift: number of bits to shift the divide value by (ie. the index of + * @shift: number of bits to left shift the divide value by (ie. the index of * the lowest bit of the divide value within its control register) + * @div: number of bits to divide the divider value by (i.e. if the + * effective divider value is the value written to the register + * multiplied by some constant) * @bits: the size of the divide value in bits * @ce_bit: the index of the change enable bit within reg, or -1 if there * isn't one @@ -87,6 +90,7 @@ struct ingenic_cgu_mux_info { struct ingenic_cgu_div_info { unsigned reg; u8 shift; + u8 div; u8 bits; s8 ce_bit; s8 busy_bit; diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 305a26c2a800..510fe7e0c8f1 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -90,51 +90,51 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { [JZ4740_CLK_PLL_HALF] = { "pll half", CGU_CLK_DIV, .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, - .div = { CGU_REG_CPCCR, 21, 1, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 }, }, [JZ4740_CLK_CCLK] = { "cclk", CGU_CLK_DIV, .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, - .div = { CGU_REG_CPCCR, 0, 4, 22, -1, -1 }, + .div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 }, }, [JZ4740_CLK_HCLK] = { "hclk", CGU_CLK_DIV, .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, - .div = { CGU_REG_CPCCR, 4, 4, 22, -1, -1 }, + .div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 }, }, [JZ4740_CLK_PCLK] = { "pclk", CGU_CLK_DIV, .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, - .div = { CGU_REG_CPCCR, 8, 4, 22, -1, -1 }, + .div = { CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1 }, }, [JZ4740_CLK_MCLK] = { "mclk", CGU_CLK_DIV, .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, - .div = { CGU_REG_CPCCR, 12, 4, 22, -1, -1 }, + .div = { CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1 }, }, [JZ4740_CLK_LCD] = { "lcd", CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, - .div = { CGU_REG_CPCCR, 16, 5, 22, -1, -1 }, + .div = { CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1 }, .gate = { CGU_REG_CLKGR, 10 }, }, [JZ4740_CLK_LCD_PCLK] = { "lcd_pclk", CGU_CLK_DIV, .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, - .div = { CGU_REG_LPCDR, 0, 11, -1, -1, -1 }, + .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, }, [JZ4740_CLK_I2S] = { "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 31, 1 }, - .div = { CGU_REG_I2SCDR, 0, 8, -1, -1, -1 }, + .div = { CGU_REG_I2SCDR, 0, 1, 8, -1, -1, -1 }, .gate = { CGU_REG_CLKGR, 6 }, }, @@ -142,21 +142,21 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 }, .mux = { CGU_REG_SSICDR, 31, 1 }, - .div = { CGU_REG_SSICDR, 0, 4, -1, -1, -1 }, + .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 }, .gate = { CGU_REG_CLKGR, 4 }, }, [JZ4740_CLK_MMC] = { "mmc", CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, - .div = { CGU_REG_MSCCDR, 0, 5, -1, -1, -1 }, + .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 }, .gate = { CGU_REG_CLKGR, 7 }, }, [JZ4740_CLK_UHC] = { "uhc", CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, - .div = { CGU_REG_UHCCDR, 0, 4, -1, -1, -1 }, + .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 }, .gate = { CGU_REG_CLKGR, 14 }, }, @@ -164,7 +164,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { "udc", CGU_CLK_MUX | CGU_CLK_DIV, .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 29, 1 }, - .div = { CGU_REG_CPCCR, 23, 6, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, .gate = { CGU_REG_SCR, 6 }, }, diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c index 431f962300b6..b35d6d9dd5aa 100644 --- a/drivers/clk/ingenic/jz4780-cgu.c +++ b/drivers/clk/ingenic/jz4780-cgu.c @@ -296,13 +296,13 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { [JZ4780_CLK_CPU] = { "cpu", CGU_CLK_DIV, .parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 }, - .div = { CGU_REG_CLOCKCONTROL, 0, 4, 22, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 0, 1, 4, 22, -1, -1 }, }, [JZ4780_CLK_L2CACHE] = { "l2cache", CGU_CLK_DIV, .parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 }, - .div = { CGU_REG_CLOCKCONTROL, 4, 4, -1, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 4, 1, 4, -1, -1, -1 }, }, [JZ4780_CLK_AHB0] = { @@ -310,7 +310,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_EPLL }, .mux = { CGU_REG_CLOCKCONTROL, 26, 2 }, - .div = { CGU_REG_CLOCKCONTROL, 8, 4, 21, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 8, 1, 4, 21, -1, -1 }, }, [JZ4780_CLK_AHB2PMUX] = { @@ -323,20 +323,20 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { [JZ4780_CLK_AHB2] = { "ahb2", CGU_CLK_DIV, .parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 }, - .div = { CGU_REG_CLOCKCONTROL, 12, 4, 20, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 12, 1, 4, 20, -1, -1 }, }, [JZ4780_CLK_PCLK] = { "pclk", CGU_CLK_DIV, .parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 }, - .div = { CGU_REG_CLOCKCONTROL, 16, 4, 20, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 16, 1, 4, 20, -1, -1 }, }, [JZ4780_CLK_DDR] = { "ddr", CGU_CLK_MUX | CGU_CLK_DIV, .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 }, .mux = { CGU_REG_DDRCDR, 30, 2 }, - .div = { CGU_REG_DDRCDR, 0, 4, 29, 28, 27 }, + .div = { CGU_REG_DDRCDR, 0, 1, 4, 29, 28, 27 }, }, [JZ4780_CLK_VPU] = { @@ -344,7 +344,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_EPLL, -1 }, .mux = { CGU_REG_VPUCDR, 30, 2 }, - .div = { CGU_REG_VPUCDR, 0, 4, 29, 28, 27 }, + .div = { CGU_REG_VPUCDR, 0, 1, 4, 29, 28, 27 }, .gate = { CGU_REG_CLKGR1, 2 }, }, @@ -352,7 +352,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { "i2s_pll", CGU_CLK_MUX | CGU_CLK_DIV, .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_EPLL, -1, -1 }, .mux = { CGU_REG_I2SCDR, 30, 1 }, - .div = { CGU_REG_I2SCDR, 0, 8, 29, 28, 27 }, + .div = { CGU_REG_I2SCDR, 0, 1, 8, 29, 28, 27 }, }, [JZ4780_CLK_I2S] = { @@ -366,7 +366,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_VPLL, -1 }, .mux = { CGU_REG_LP0CDR, 30, 2 }, - .div = { CGU_REG_LP0CDR, 0, 8, 28, 27, 26 }, + .div = { CGU_REG_LP0CDR, 0, 1, 8, 28, 27, 26 }, }, [JZ4780_CLK_LCD1PIXCLK] = { @@ -374,7 +374,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_VPLL, -1 }, .mux = { CGU_REG_LP1CDR, 30, 2 }, - .div = { CGU_REG_LP1CDR, 0, 8, 28, 27, 26 }, + .div = { CGU_REG_LP1CDR, 0, 1, 8, 28, 27, 26 }, }, [JZ4780_CLK_MSCMUX] = { @@ -386,21 +386,21 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { [JZ4780_CLK_MSC0] = { "msc0", CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, - .div = { CGU_REG_MSC0CDR, 0, 8, 29, 28, 27 }, + .div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 }, .gate = { CGU_REG_CLKGR0, 3 }, }, [JZ4780_CLK_MSC1] = { "msc1", CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, - .div = { CGU_REG_MSC1CDR, 0, 8, 29, 28, 27 }, + .div = { CGU_REG_MSC1CDR, 0, 2, 8, 29, 28, 27 }, .gate = { CGU_REG_CLKGR0, 11 }, }, [JZ4780_CLK_MSC2] = { "msc2", CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, - .div = { CGU_REG_MSC2CDR, 0, 8, 29, 28, 27 }, + .div = { CGU_REG_MSC2CDR, 0, 2, 8, 29, 28, 27 }, .gate = { CGU_REG_CLKGR0, 12 }, }, @@ -409,7 +409,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_EPLL, JZ4780_CLK_OTGPHY }, .mux = { CGU_REG_UHCCDR, 30, 2 }, - .div = { CGU_REG_UHCCDR, 0, 8, 29, 28, 27 }, + .div = { CGU_REG_UHCCDR, 0, 1, 8, 29, 28, 27 }, .gate = { CGU_REG_CLKGR0, 24 }, }, @@ -417,7 +417,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { "ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV, .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 }, .mux = { CGU_REG_SSICDR, 30, 1 }, - .div = { CGU_REG_SSICDR, 0, 8, 29, 28, 27 }, + .div = { CGU_REG_SSICDR, 0, 1, 8, 29, 28, 27 }, }, [JZ4780_CLK_SSI] = { @@ -430,7 +430,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { "cim_mclk", CGU_CLK_MUX | CGU_CLK_DIV, .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 }, .mux = { CGU_REG_CIMCDR, 31, 1 }, - .div = { CGU_REG_CIMCDR, 0, 8, 30, 29, 28 }, + .div = { CGU_REG_CIMCDR, 0, 1, 8, 30, 29, 28 }, }, [JZ4780_CLK_PCMPLL] = { @@ -438,7 +438,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_EPLL, JZ4780_CLK_VPLL }, .mux = { CGU_REG_PCMCDR, 29, 2 }, - .div = { CGU_REG_PCMCDR, 0, 8, 28, 27, 26 }, + .div = { CGU_REG_PCMCDR, 0, 1, 8, 28, 27, 26 }, }, [JZ4780_CLK_PCM] = { @@ -453,7 +453,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_EPLL }, .mux = { CGU_REG_GPUCDR, 30, 2 }, - .div = { CGU_REG_GPUCDR, 0, 4, 29, 28, 27 }, + .div = { CGU_REG_GPUCDR, 0, 1, 4, 29, 28, 27 }, .gate = { CGU_REG_CLKGR1, 4 }, }, @@ -462,7 +462,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_VPLL, -1 }, .mux = { CGU_REG_HDMICDR, 30, 2 }, - .div = { CGU_REG_HDMICDR, 0, 8, 29, 28, 26 }, + .div = { CGU_REG_HDMICDR, 0, 1, 8, 29, 28, 26 }, .gate = { CGU_REG_CLKGR1, 9 }, }, @@ -471,7 +471,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, JZ4780_CLK_EPLL }, .mux = { CGU_REG_BCHCDR, 30, 2 }, - .div = { CGU_REG_BCHCDR, 0, 4, 29, 28, 27 }, + .div = { CGU_REG_BCHCDR, 0, 1, 4, 29, 28, 27 }, .gate = { CGU_REG_CLKGR0, 1 }, }, diff --git a/drivers/clk/meson/meson8b-clkc.c b/drivers/clk/meson/meson8b-clkc.c index 61f6d55c4ac7..4d057b3e21b2 100644 --- a/drivers/clk/meson/meson8b-clkc.c +++ b/drivers/clk/meson/meson8b-clkc.c @@ -141,11 +141,11 @@ static const struct composite_conf mali_conf __initconst = { }; static const struct clk_conf meson8b_xtal_conf __initconst = - FIXED_RATE_P(MESON8B_REG_CTL0_ADDR, CLKID_XTAL, "xtal", - CLK_IS_ROOT, PARM(0x00, 4, 7)); + FIXED_RATE_P(MESON8B_REG_CTL0_ADDR, CLKID_XTAL, "xtal", 0, + PARM(0x00, 4, 7)); static const struct clk_conf meson8b_clk_confs[] __initconst = { - FIXED_RATE(CLKID_ZERO, "zero", CLK_IS_ROOT, 0), + FIXED_RATE(CLKID_ZERO, "zero", 0, 0), PLL(MESON8B_REG_PLL_FIXED, CLKID_PLL_FIXED, "fixed_pll", p_xtal, 0, &pll_confs), PLL(MESON8B_REG_PLL_VID, CLKID_PLL_VID, "vid_pll", diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 38931dbd1eff..383f6a4f64f0 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -99,23 +99,19 @@ void __init mmp2_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys, return; } - clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200); + clk = clk_register_fixed_rate(NULL, "clk32", NULL, 0, 3200); clk_register_clkdev(clk, "clk32", NULL); - vctcxo = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT, - 26000000); + vctcxo = clk_register_fixed_rate(NULL, "vctcxo", NULL, 0, 26000000); clk_register_clkdev(vctcxo, "vctcxo", NULL); - clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT, - 800000000); + clk = clk_register_fixed_rate(NULL, "pll1", NULL, 0, 800000000); clk_register_clkdev(clk, "pll1", NULL); - clk = clk_register_fixed_rate(NULL, "usb_pll", NULL, CLK_IS_ROOT, - 480000000); + clk = clk_register_fixed_rate(NULL, "usb_pll", NULL, 0, 480000000); clk_register_clkdev(clk, "usb_pll", NULL); - clk = clk_register_fixed_rate(NULL, "pll2", NULL, CLK_IS_ROOT, - 960000000); + clk = clk_register_fixed_rate(NULL, "pll2", NULL, 0, 960000000); clk_register_clkdev(clk, "pll2", NULL); clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1", diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 251533d87c65..3a51fff1b0e7 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -63,11 +63,11 @@ struct mmp2_clk_unit { }; static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { - {MMP2_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, - {MMP2_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, - {MMP2_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 800000000}, - {MMP2_CLK_PLL2, "pll2", NULL, CLK_IS_ROOT, 960000000}, - {MMP2_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, + {MMP2_CLK_CLK32, "clk32", NULL, 0, 32768}, + {MMP2_CLK_VCTCXO, "vctcxo", NULL, 0, 26000000}, + {MMP2_CLK_PLL1, "pll1", NULL, 0, 800000000}, + {MMP2_CLK_PLL2, "pll2", NULL, 0, 960000000}, + {MMP2_CLK_USB_PLL, "usb_pll", NULL, 0, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 64eaf4141c69..87f2317b2a00 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -56,10 +56,10 @@ struct pxa168_clk_unit { }; static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { - {PXA168_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, - {PXA168_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, - {PXA168_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, - {PXA168_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, + {PXA168_CLK_CLK32, "clk32", NULL, 0, 32768}, + {PXA168_CLK_VCTCXO, "vctcxo", NULL, 0, 26000000}, + {PXA168_CLK_PLL1, "pll1", NULL, 0, 624000000}, + {PXA168_CLK_USB_PLL, "usb_pll", NULL, 0, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c index 433a5ae1eae0..e478ff44e170 100644 --- a/drivers/clk/mmp/clk-of-pxa1928.c +++ b/drivers/clk/mmp/clk-of-pxa1928.c @@ -34,12 +34,12 @@ struct pxa1928_clk_unit { }; static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { - {0, "clk32", NULL, CLK_IS_ROOT, 32768}, - {0, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, - {0, "pll1_624", NULL, CLK_IS_ROOT, 624000000}, - {0, "pll5p", NULL, CLK_IS_ROOT, 832000000}, - {0, "pll5", NULL, CLK_IS_ROOT, 1248000000}, - {0, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, + {0, "clk32", NULL, 0, 32768}, + {0, "vctcxo", NULL, 0, 26000000}, + {0, "pll1_624", NULL, 0, 624000000}, + {0, "pll5p", NULL, 0, 832000000}, + {0, "pll5", NULL, 0, 1248000000}, + {0, "usb_pll", NULL, 0, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 13d6173326a4..e22a67f76d93 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -56,10 +56,10 @@ struct pxa910_clk_unit { }; static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { - {PXA910_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, - {PXA910_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, - {PXA910_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, - {PXA910_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, + {PXA910_CLK_CLK32, "clk32", NULL, 0, 32768}, + {PXA910_CLK_VCTCXO, "vctcxo", NULL, 0, 26000000}, + {PXA910_CLK_PLL1, "pll1", NULL, 0, 624000000}, + {PXA910_CLK_USB_PLL, "usb_pll", NULL, 0, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index 0dd83fb950c9..a9ef9209532a 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -92,15 +92,13 @@ void __init pxa168_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys, return; } - clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200); + clk = clk_register_fixed_rate(NULL, "clk32", NULL, 0, 3200); clk_register_clkdev(clk, "clk32", NULL); - clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT, - 26000000); + clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, 0, 26000000); clk_register_clkdev(clk, "vctcxo", NULL); - clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT, - 624000000); + clk = clk_register_fixed_rate(NULL, "pll1", NULL, 0, 624000000); clk_register_clkdev(clk, "pll1", NULL); clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1", diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index e1d2ce22cdf1..a520cf7702a1 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -97,15 +97,13 @@ void __init pxa910_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys, return; } - clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200); + clk = clk_register_fixed_rate(NULL, "clk32", NULL, 0, 3200); clk_register_clkdev(clk, "clk32", NULL); - clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT, - 26000000); + clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, 0, 26000000); clk_register_clkdev(clk, "vctcxo", NULL); - clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT, - 624000000); + clk = clk_register_fixed_rate(NULL, "pll1", NULL, 0, 624000000); clk_register_clkdev(clk, "pll1", NULL); clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1", diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index eaee8f099c8c..3165da77d525 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -29,6 +29,12 @@ config ARMADA_XP_CLK select MVEBU_CLK_COMMON select MVEBU_CLK_CPU +config ARMADA_AP806_SYSCON + bool + +config ARMADA_CP110_SYSCON + bool + config DOVE_CLK bool select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 8866115486f7..7172ef65693d 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o +obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o +obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o obj-$(CONFIG_ORION_CLK) += orion.o diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c new file mode 100644 index 000000000000..02023baf86c9 --- /dev/null +++ b/drivers/clk/mvebu/ap806-system-controller.c @@ -0,0 +1,168 @@ +/* + * Marvell Armada AP806 System Controller + * + * Copyright (C) 2016 Marvell + * + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) "ap806-system-controller: " fmt + +#include <linux/clk-provider.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define AP806_SAR_REG 0x400 +#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f + +#define AP806_CLK_NUM 4 + +static struct clk *ap806_clks[AP806_CLK_NUM]; + +static struct clk_onecell_data ap806_clk_data = { + .clks = ap806_clks, + .clk_num = AP806_CLK_NUM, +}; + +static int ap806_syscon_clk_probe(struct platform_device *pdev) +{ + unsigned int freq_mode, cpuclk_freq; + const char *name, *fixedclk_name; + struct device_node *np = pdev->dev.of_node; + struct regmap *regmap; + u32 reg; + int ret; + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "cannot get regmap\n"); + return PTR_ERR(regmap); + } + + ret = regmap_read(regmap, AP806_SAR_REG, ®); + if (ret) { + dev_err(&pdev->dev, "cannot read from regmap\n"); + return ret; + } + + freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK; + switch (freq_mode) { + case 0x0 ... 0x5: + cpuclk_freq = 2000; + break; + case 0x6 ... 0xB: + cpuclk_freq = 1800; + break; + case 0xC ... 0x11: + cpuclk_freq = 1600; + break; + case 0x12 ... 0x16: + cpuclk_freq = 1400; + break; + case 0x17 ... 0x19: + cpuclk_freq = 1300; + break; + default: + dev_err(&pdev->dev, "invalid SAR value\n"); + return -EINVAL; + } + + /* Convert to hertz */ + cpuclk_freq *= 1000 * 1000; + + /* CPU clocks depend on the Sample At Reset configuration */ + of_property_read_string_index(np, "clock-output-names", + 0, &name); + ap806_clks[0] = clk_register_fixed_rate(&pdev->dev, name, NULL, + 0, cpuclk_freq); + if (IS_ERR(ap806_clks[0])) { + ret = PTR_ERR(ap806_clks[0]); + goto fail0; + } + + of_property_read_string_index(np, "clock-output-names", + 1, &name); + ap806_clks[1] = clk_register_fixed_rate(&pdev->dev, name, NULL, 0, + cpuclk_freq); + if (IS_ERR(ap806_clks[1])) { + ret = PTR_ERR(ap806_clks[1]); + goto fail1; + } + + /* Fixed clock is always 1200 Mhz */ + of_property_read_string_index(np, "clock-output-names", + 2, &fixedclk_name); + ap806_clks[2] = clk_register_fixed_rate(&pdev->dev, fixedclk_name, NULL, + 0, 1200 * 1000 * 1000); + if (IS_ERR(ap806_clks[2])) { + ret = PTR_ERR(ap806_clks[2]); + goto fail2; + } + + /* MSS Clock is fixed clock divided by 6 */ + of_property_read_string_index(np, "clock-output-names", + 3, &name); + ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name, + 0, 1, 6); + if (IS_ERR(ap806_clks[3])) { + ret = PTR_ERR(ap806_clks[3]); + goto fail3; + } + + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data); + if (ret) + goto fail_clk_add; + + return 0; + +fail_clk_add: + clk_unregister_fixed_factor(ap806_clks[3]); +fail3: + clk_unregister_fixed_rate(ap806_clks[2]); +fail2: + clk_unregister_fixed_rate(ap806_clks[1]); +fail1: + clk_unregister_fixed_rate(ap806_clks[0]); +fail0: + return ret; +} + +static int ap806_syscon_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + clk_unregister_fixed_factor(ap806_clks[3]); + clk_unregister_fixed_rate(ap806_clks[2]); + clk_unregister_fixed_rate(ap806_clks[1]); + clk_unregister_fixed_rate(ap806_clks[0]); + + return 0; +} + +static const struct of_device_id ap806_syscon_of_match[] = { + { .compatible = "marvell,ap806-system-controller", }, + { } +}; +MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match); + +static struct platform_driver ap806_syscon_driver = { + .probe = ap806_syscon_clk_probe, + .remove = ap806_syscon_clk_remove, + .driver = { + .name = "marvell-ap806-system-controller", + .of_match_table = ap806_syscon_of_match, + }, +}; + +module_platform_driver(ap806_syscon_driver); + +MODULE_DESCRIPTION("Marvell AP806 System Controller driver"); +MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c new file mode 100644 index 000000000000..7fa42d6b2b92 --- /dev/null +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -0,0 +1,406 @@ +/* + * Marvell Armada CP110 System Controller + * + * Copyright (C) 2016 Marvell + * + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +/* + * CP110 has 5 core clocks: + * + * - APLL (1 Ghz) + * - PPv2 core (1/3 APLL) + * - EIP (1/2 APLL) + * - Core (1/2 EIP) + * + * - NAND clock, which is either: + * - Equal to the core clock + * - 2/5 APLL + * + * CP110 has 32 gatable clocks, for the various peripherals in the + * IP. They have fairly complicated parent/child relationships. + */ + +#define pr_fmt(fmt) "cp110-system-controller: " fmt + +#include <linux/clk-provider.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define CP110_PM_CLOCK_GATING_REG 0x220 +#define CP110_NAND_FLASH_CLK_CTRL_REG 0x700 +#define NF_CLOCK_SEL_400_MASK BIT(0) + +enum { + CP110_CLK_TYPE_CORE, + CP110_CLK_TYPE_GATABLE, +}; + +#define CP110_MAX_CORE_CLOCKS 5 +#define CP110_MAX_GATABLE_CLOCKS 32 + +#define CP110_CLK_NUM \ + (CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS) + +#define CP110_CORE_APLL 0 +#define CP110_CORE_PPV2 1 +#define CP110_CORE_EIP 2 +#define CP110_CORE_CORE 3 +#define CP110_CORE_NAND 4 + +/* A number of gatable clocks need special handling */ +#define CP110_GATE_AUDIO 0 +#define CP110_GATE_COMM_UNIT 1 +#define CP110_GATE_NAND 2 +#define CP110_GATE_PPV2 3 +#define CP110_GATE_SDIO 4 +#define CP110_GATE_XOR1 7 +#define CP110_GATE_XOR0 8 +#define CP110_GATE_PCIE_X1_0 11 +#define CP110_GATE_PCIE_X1_1 12 +#define CP110_GATE_PCIE_X4 13 +#define CP110_GATE_PCIE_XOR 14 +#define CP110_GATE_SATA 15 +#define CP110_GATE_SATA_USB 16 +#define CP110_GATE_MAIN 17 +#define CP110_GATE_SDMMC 18 +#define CP110_GATE_SLOW_IO 21 +#define CP110_GATE_USB3H0 22 +#define CP110_GATE_USB3H1 23 +#define CP110_GATE_USB3DEV 24 +#define CP110_GATE_EIP150 25 +#define CP110_GATE_EIP197 26 + +static struct clk *cp110_clks[CP110_CLK_NUM]; + +static struct clk_onecell_data cp110_clk_data = { + .clks = cp110_clks, + .clk_num = CP110_CLK_NUM, +}; + +struct cp110_gate_clk { + struct clk_hw hw; + struct regmap *regmap; + u8 bit_idx; +}; + +#define to_cp110_gate_clk(clk) container_of(clk, struct cp110_gate_clk, hw) + +static int cp110_gate_enable(struct clk_hw *hw) +{ + struct cp110_gate_clk *gate = to_cp110_gate_clk(hw); + + regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG, + BIT(gate->bit_idx), BIT(gate->bit_idx)); + + return 0; +} + +static void cp110_gate_disable(struct clk_hw *hw) +{ + struct cp110_gate_clk *gate = to_cp110_gate_clk(hw); + + regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG, + BIT(gate->bit_idx), 0); +} + +static int cp110_gate_is_enabled(struct clk_hw *hw) +{ + struct cp110_gate_clk *gate = to_cp110_gate_clk(hw); + u32 val; + + regmap_read(gate->regmap, CP110_PM_CLOCK_GATING_REG, &val); + + return val & BIT(gate->bit_idx); +} + +static const struct clk_ops cp110_gate_ops = { + .enable = cp110_gate_enable, + .disable = cp110_gate_disable, + .is_enabled = cp110_gate_is_enabled, +}; + +static struct clk *cp110_register_gate(const char *name, + const char *parent_name, + struct regmap *regmap, u8 bit_idx) +{ + struct cp110_gate_clk *gate; + struct clk *clk; + struct clk_init_data init; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &cp110_gate_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + gate->regmap = regmap; + gate->bit_idx = bit_idx; + gate->hw.init = &init; + + clk = clk_register(NULL, &gate->hw); + if (IS_ERR(clk)) + kfree(gate); + + return clk; +} + +static void cp110_unregister_gate(struct clk *clk) +{ + struct clk_hw *hw; + + hw = __clk_get_hw(clk); + if (!hw) + return; + + clk_unregister(clk); + kfree(to_cp110_gate_clk(hw)); +} + +static struct clk *cp110_of_clk_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_onecell_data *clk_data = data; + unsigned int type = clkspec->args[0]; + unsigned int idx = clkspec->args[1]; + + if (type == CP110_CLK_TYPE_CORE) { + if (idx > CP110_MAX_CORE_CLOCKS) + return ERR_PTR(-EINVAL); + return clk_data->clks[idx]; + } else if (type == CP110_CLK_TYPE_GATABLE) { + if (idx > CP110_MAX_GATABLE_CLOCKS) + return ERR_PTR(-EINVAL); + return clk_data->clks[CP110_MAX_CORE_CLOCKS + idx]; + } + + return ERR_PTR(-EINVAL); +} + +static int cp110_syscon_clk_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct device_node *np = pdev->dev.of_node; + const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name; + struct clk *clk; + u32 nand_clk_ctrl; + int i, ret; + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_read(regmap, CP110_NAND_FLASH_CLK_CTRL_REG, + &nand_clk_ctrl); + if (ret) + return ret; + + /* Register the APLL which is the root of the clk tree */ + of_property_read_string_index(np, "core-clock-output-names", + CP110_CORE_APLL, &apll_name); + clk = clk_register_fixed_rate(NULL, apll_name, NULL, 0, + 1000 * 1000 * 1000); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail0; + } + + cp110_clks[CP110_CORE_APLL] = clk; + + /* PPv2 is APLL/3 */ + of_property_read_string_index(np, "core-clock-output-names", + CP110_CORE_PPV2, &ppv2_name); + clk = clk_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail1; + } + + cp110_clks[CP110_CORE_PPV2] = clk; + + /* EIP clock is APLL/2 */ + of_property_read_string_index(np, "core-clock-output-names", + CP110_CORE_EIP, &eip_name); + clk = clk_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail2; + } + + cp110_clks[CP110_CORE_EIP] = clk; + + /* Core clock is EIP/2 */ + of_property_read_string_index(np, "core-clock-output-names", + CP110_CORE_CORE, &core_name); + clk = clk_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail3; + } + + cp110_clks[CP110_CORE_CORE] = clk; + + /* NAND can be either APLL/2.5 or core clock */ + of_property_read_string_index(np, "core-clock-output-names", + CP110_CORE_NAND, &nand_name); + if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK) + clk = clk_register_fixed_factor(NULL, nand_name, + apll_name, 0, 2, 5); + else + clk = clk_register_fixed_factor(NULL, nand_name, + core_name, 0, 1, 1); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail4; + } + + cp110_clks[CP110_CORE_NAND] = clk; + + for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { + const char *parent, *name; + int ret; + + ret = of_property_read_string_index(np, + "gate-clock-output-names", + i, &name); + /* Reached the end of the list? */ + if (ret < 0) + break; + + if (!strcmp(name, "none")) + continue; + + switch (i) { + case CP110_GATE_AUDIO: + case CP110_GATE_COMM_UNIT: + case CP110_GATE_EIP150: + case CP110_GATE_EIP197: + case CP110_GATE_SLOW_IO: + of_property_read_string_index(np, + "gate-clock-output-names", + CP110_GATE_MAIN, &parent); + break; + case CP110_GATE_NAND: + parent = nand_name; + break; + case CP110_GATE_PPV2: + parent = ppv2_name; + break; + case CP110_GATE_SDIO: + of_property_read_string_index(np, + "gate-clock-output-names", + CP110_GATE_SDMMC, &parent); + break; + case CP110_GATE_XOR1: + case CP110_GATE_XOR0: + case CP110_GATE_PCIE_X1_0: + case CP110_GATE_PCIE_X1_1: + case CP110_GATE_PCIE_X4: + of_property_read_string_index(np, + "gate-clock-output-names", + CP110_GATE_PCIE_XOR, &parent); + break; + case CP110_GATE_SATA: + case CP110_GATE_USB3H0: + case CP110_GATE_USB3H1: + case CP110_GATE_USB3DEV: + of_property_read_string_index(np, + "gate-clock-output-names", + CP110_GATE_SATA_USB, &parent); + break; + default: + parent = core_name; + break; + } + + clk = cp110_register_gate(name, parent, regmap, i); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail_gate; + } + + cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk; + } + + ret = of_clk_add_provider(np, cp110_of_clk_get, &cp110_clk_data); + if (ret) + goto fail_clk_add; + + return 0; + +fail_clk_add: +fail_gate: + for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { + clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; + + if (clk) + cp110_unregister_gate(clk); + } + + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); +fail4: + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); +fail3: + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); +fail2: + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); +fail1: + clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); +fail0: + return ret; +} + +static int cp110_syscon_clk_remove(struct platform_device *pdev) +{ + int i; + + of_clk_del_provider(pdev->dev.of_node); + + for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { + struct clk *clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; + + if (clk) + cp110_unregister_gate(clk); + } + + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); + clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); + clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); + + return 0; +} + +static const struct of_device_id cp110_syscon_of_match[] = { + { .compatible = "marvell,cp110-system-controller0", }, + { } +}; +MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match); + +static struct platform_driver cp110_syscon_driver = { + .probe = cp110_syscon_clk_probe, + .remove = cp110_syscon_clk_remove, + .driver = { + .name = "marvell-cp110-system-controller0", + .of_match_table = cp110_syscon_of_match, + }, +}; + +module_platform_driver(cp110_syscon_driver); + +MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver"); +MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/nxp/clk-lpc18xx-creg.c b/drivers/clk/nxp/clk-lpc18xx-creg.c index d44b61afa2dc..9e35749dafdf 100644 --- a/drivers/clk/nxp/clk-lpc18xx-creg.c +++ b/drivers/clk/nxp/clk-lpc18xx-creg.c @@ -147,6 +147,7 @@ static struct clk *clk_register_creg_clk(struct device *dev, init.name = creg_clk->name; init.parent_names = parent_name; init.num_parents = 1; + init.flags = 0; creg_clk->reg = syscon; creg_clk->hw.init = &init; diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 9c29080a84d8..5c4e193164d4 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -2346,6 +2346,7 @@ static struct clk_branch gcc_crypto_ahb_clk = { "pcnoc_bfdcd_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2381,6 +2382,7 @@ static struct clk_branch gcc_crypto_clk = { "crypto_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index 6df7ff36b416..847dd9dadeca 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -1279,21 +1279,6 @@ static struct clk_branch mmss_misc_cxo_clk = { }, }; -static struct clk_branch mmss_mmagic_axi_clk = { - .halt_reg = 0x506c, - .clkr = { - .enable_reg = 0x506c, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "mmss_mmagic_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch mmss_mmagic_maxi_clk = { .halt_reg = 0x5074, .clkr = { @@ -1579,21 +1564,6 @@ static struct clk_branch smmu_video_axi_clk = { }, }; -static struct clk_branch mmagic_bimc_axi_clk = { - .halt_reg = 0x5294, - .clkr = { - .enable_reg = 0x5294, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "mmagic_bimc_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch mmagic_bimc_noc_cfg_ahb_clk = { .halt_reg = 0x5298, .clkr = { @@ -3121,7 +3091,6 @@ static struct clk_regmap *mmcc_msm8996_clocks[] = { [MMSS_MMAGIC_CFG_AHB_CLK] = &mmss_mmagic_cfg_ahb_clk.clkr, [MMSS_MISC_AHB_CLK] = &mmss_misc_ahb_clk.clkr, [MMSS_MISC_CXO_CLK] = &mmss_misc_cxo_clk.clkr, - [MMSS_MMAGIC_AXI_CLK] = &mmss_mmagic_axi_clk.clkr, [MMSS_MMAGIC_MAXI_CLK] = &mmss_mmagic_maxi_clk.clkr, [MMAGIC_CAMSS_AXI_CLK] = &mmagic_camss_axi_clk.clkr, [MMAGIC_CAMSS_NOC_CFG_AHB_CLK] = &mmagic_camss_noc_cfg_ahb_clk.clkr, @@ -3141,7 +3110,6 @@ static struct clk_regmap *mmcc_msm8996_clocks[] = { [MMAGIC_VIDEO_NOC_CFG_AHB_CLK] = &mmagic_video_noc_cfg_ahb_clk.clkr, [SMMU_VIDEO_AHB_CLK] = &smmu_video_ahb_clk.clkr, [SMMU_VIDEO_AXI_CLK] = &smmu_video_axi_clk.clkr, - [MMAGIC_BIMC_AXI_CLK] = &mmagic_bimc_axi_clk.clkr, [MMAGIC_BIMC_NOC_CFG_AHB_CLK] = &mmagic_bimc_noc_cfg_ahb_clk.clkr, [GPU_GX_GFX3D_CLK] = &gpu_gx_gfx3d_clk.clkr, [GPU_GX_RBBMTIMER_CLK] = &gpu_gx_rbbmtimer_clk.clkr, diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index 8b597b9a3804..5093a250650d 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -316,11 +316,10 @@ void __init cpg_mstp_add_clk_domain(struct device_node *np) return; pd->name = np->name; - pd->flags = GENPD_FLAG_PM_CLK; - pm_genpd_init(pd, &simple_qos_governor, false); pd->attach_dev = cpg_mstp_attach_dev; pd->detach_dev = cpg_mstp_detach_dev; + pm_genpd_init(pd, &pm_domain_always_on_gov, false); of_genpd_add_provider_simple(np, pd); } diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index 6af7f5b6e824..ca5519c583d4 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -120,6 +120,7 @@ static const struct cpg_core_clk r8a7795_core_clks[] __initconst = { DEF_DIV6P1("mso", R8A7795_CLK_MSO, CLK_PLL1_DIV4, 0x014), DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV2, 0x250), DEF_DIV6P1("canfd", R8A7795_CLK_CANFD, CLK_PLL1_DIV4, 0x244), + DEF_DIV6P1("csi0", R8A7795_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), DEF_DIV6_RO("osc", R8A7795_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8), DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32), @@ -190,6 +191,10 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { DEF_MOD("ehci1", 702, R8A7795_CLK_S3D4), DEF_MOD("ehci0", 703, R8A7795_CLK_S3D4), DEF_MOD("hsusb", 704, R8A7795_CLK_S3D4), + DEF_MOD("csi21", 713, R8A7795_CLK_CSI0), + DEF_MOD("csi20", 714, R8A7795_CLK_CSI0), + DEF_MOD("csi41", 715, R8A7795_CLK_CSI0), + DEF_MOD("csi40", 716, R8A7795_CLK_CSI0), DEF_MOD("du3", 721, R8A7795_CLK_S2D1), DEF_MOD("du2", 722, R8A7795_CLK_S2D1), DEF_MOD("du1", 723, R8A7795_CLK_S2D1), @@ -197,6 +202,14 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { DEF_MOD("lvds", 727, R8A7795_CLK_S2D1), DEF_MOD("hdmi1", 728, R8A7795_CLK_HDMI), DEF_MOD("hdmi0", 729, R8A7795_CLK_HDMI), + DEF_MOD("vin7", 804, R8A7795_CLK_S2D1), + DEF_MOD("vin6", 805, R8A7795_CLK_S2D1), + DEF_MOD("vin5", 806, R8A7795_CLK_S2D1), + DEF_MOD("vin4", 807, R8A7795_CLK_S2D1), + DEF_MOD("vin3", 808, R8A7795_CLK_S2D1), + DEF_MOD("vin2", 809, R8A7795_CLK_S2D1), + DEF_MOD("vin1", 810, R8A7795_CLK_S2D1), + DEF_MOD("vin0", 811, R8A7795_CLK_S2D1), DEF_MOD("etheravb", 812, R8A7795_CLK_S3D2), DEF_MOD("sata0", 815, R8A7795_CLK_S3D2), DEF_MOD("gpio7", 905, R8A7795_CLK_CP), diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 1f2dc3629f0e..210cd744a7a9 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -493,9 +493,9 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev, genpd = &pd->genpd; genpd->name = np->name; genpd->flags = GENPD_FLAG_PM_CLK; - pm_genpd_init(genpd, &simple_qos_governor, false); genpd->attach_dev = cpg_mssr_attach_dev; genpd->detach_dev = cpg_mssr_detach_dev; + pm_genpd_init(genpd, &pm_domain_always_on_gov, false); cpg_mssr_clk_domain = pd; of_genpd_add_provider_simple(np, genpd); diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 80b9a379beb4..f47a2fa962d2 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -15,3 +15,4 @@ obj-y += clk-rk3188.o obj-y += clk-rk3228.o obj-y += clk-rk3288.o obj-y += clk-rk3368.o +obj-y += clk-rk3399.o diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 4e73ed5cab58..4bb130cd0062 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -158,12 +158,16 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask, reg_data->div_core_shift) | - HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), + HIWORD_UPDATE(reg_data->mux_core_alt, + reg_data->mux_core_mask, + reg_data->mux_core_shift), cpuclk->reg_base + reg_data->core_reg); } else { /* select alternate parent */ - writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), - cpuclk->reg_base + reg_data->core_reg); + writel(HIWORD_UPDATE(reg_data->mux_core_alt, + reg_data->mux_core_mask, + reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); } spin_unlock_irqrestore(cpuclk->lock, flags); @@ -198,7 +202,9 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, writel(HIWORD_UPDATE(0, reg_data->div_core_mask, reg_data->div_core_shift) | - HIWORD_UPDATE(0, 1, reg_data->mux_core_shift), + HIWORD_UPDATE(reg_data->mux_core_main, + reg_data->mux_core_mask, + reg_data->mux_core_shift), cpuclk->reg_base + reg_data->core_reg); if (ndata->old_rate > ndata->new_rate) @@ -252,7 +258,7 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, return ERR_PTR(-ENOMEM); init.name = name; - init.parent_names = &parent_names[0]; + init.parent_names = &parent_names[reg_data->mux_core_main]; init.num_parents = 1; init.ops = &rockchip_cpuclk_ops; @@ -270,10 +276,10 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb; cpuclk->hw.init = &init; - cpuclk->alt_parent = __clk_lookup(parent_names[1]); + cpuclk->alt_parent = __clk_lookup(parent_names[reg_data->mux_core_alt]); if (!cpuclk->alt_parent) { - pr_err("%s: could not lookup alternate parent\n", - __func__); + pr_err("%s: could not lookup alternate parent: (%d)\n", + __func__, reg_data->mux_core_alt); ret = -EINVAL; goto free_cpuclk; } @@ -285,10 +291,11 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, goto free_cpuclk; } - clk = __clk_lookup(parent_names[0]); + clk = __clk_lookup(parent_names[reg_data->mux_core_main]); if (!clk) { - pr_err("%s: could not lookup parent clock %s\n", - __func__, parent_names[0]); + pr_err("%s: could not lookup parent clock: (%d) %s\n", + __func__, reg_data->mux_core_main, + parent_names[reg_data->mux_core_main]); ret = -EINVAL; goto free_alt_parent; } diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index e0dc7e83403a..bc856f21f6b2 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -123,7 +123,8 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; raw_value |= nineties; - writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift), mmc_clock->reg); + writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift), + mmc_clock->reg); pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d\n", clk_hw_get_name(hw), degrees, delay_num, diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 5de797e34d54..db81e454166b 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -46,6 +46,8 @@ struct rockchip_clk_pll { const struct rockchip_pll_rate_table *rate_table; unsigned int rate_count; spinlock_t *lock; + + struct rockchip_clk_provider *ctx; }; #define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw) @@ -90,15 +92,10 @@ static long rockchip_pll_round_rate(struct clk_hw *hw, */ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) { - struct regmap *grf = rockchip_clk_get_grf(); + struct regmap *grf = pll->ctx->grf; unsigned int val; int delay = 24000000, ret; - if (IS_ERR(grf)) { - pr_err("%s: grf regmap not available\n", __func__); - return PTR_ERR(grf); - } - while (delay > 0) { ret = regmap_read(grf, pll->lock_offset, &val); if (ret) { @@ -234,7 +231,7 @@ static int rockchip_rk3036_pll_set_params(struct rockchip_clk_pll *pll, /* wait for the pll to lock */ ret = rockchip_pll_wait_lock(pll); if (ret) { - pr_warn("%s: pll update unsucessful, trying to restore old params\n", + pr_warn("%s: pll update unsuccessful, trying to restore old params\n", __func__); rockchip_rk3036_pll_set_params(pll, &cur); } @@ -250,17 +247,9 @@ static int rockchip_rk3036_pll_set_rate(struct clk_hw *hw, unsigned long drate, { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); const struct rockchip_pll_rate_table *rate; - unsigned long old_rate = rockchip_rk3036_pll_recalc_rate(hw, prate); - struct regmap *grf = rockchip_clk_get_grf(); - if (IS_ERR(grf)) { - pr_debug("%s: grf regmap not available, aborting rate change\n", - __func__); - return PTR_ERR(grf); - } - - pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", - __func__, __clk_get_name(hw->clk), old_rate, drate, prate); + pr_debug("%s: changing %s to %lu with a parent rate of %lu\n", + __func__, __clk_get_name(hw->clk), drate, prate); /* Get required rate settings from table */ rate = rockchip_get_pll_settings(pll, drate); @@ -473,7 +462,7 @@ static int rockchip_rk3066_pll_set_params(struct rockchip_clk_pll *pll, /* wait for the pll to lock */ ret = rockchip_pll_wait_lock(pll); if (ret) { - pr_warn("%s: pll update unsucessful, trying to restore old params\n", + pr_warn("%s: pll update unsuccessful, trying to restore old params\n", __func__); rockchip_rk3066_pll_set_params(pll, &cur); } @@ -489,17 +478,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); const struct rockchip_pll_rate_table *rate; - unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); - struct regmap *grf = rockchip_clk_get_grf(); - if (IS_ERR(grf)) { - pr_debug("%s: grf regmap not available, aborting rate change\n", - __func__); - return PTR_ERR(grf); - } - - pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", - __func__, clk_hw_get_name(hw), old_rate, drate, prate); + pr_debug("%s: changing %s to %lu with a parent rate of %lu\n", + __func__, clk_hw_get_name(hw), drate, prate); /* Get required rate settings from table */ rate = rockchip_get_pll_settings(pll, drate); @@ -563,11 +544,6 @@ static void rockchip_rk3066_pll_init(struct clk_hw *hw) rate->no, cur.no, rate->nf, cur.nf, rate->nb, cur.nb); if (rate->nr != cur.nr || rate->no != cur.no || rate->nf != cur.nf || rate->nb != cur.nb) { - struct regmap *grf = rockchip_clk_get_grf(); - - if (IS_ERR(grf)) - return; - pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n", __func__, clk_hw_get_name(hw)); rockchip_rk3066_pll_set_params(pll, rate); @@ -591,16 +567,277 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { .init = rockchip_rk3066_pll_init, }; +/** + * PLL used in RK3399 + */ + +#define RK3399_PLLCON(i) (i * 0x4) +#define RK3399_PLLCON0_FBDIV_MASK 0xfff +#define RK3399_PLLCON0_FBDIV_SHIFT 0 +#define RK3399_PLLCON1_REFDIV_MASK 0x3f +#define RK3399_PLLCON1_REFDIV_SHIFT 0 +#define RK3399_PLLCON1_POSTDIV1_MASK 0x7 +#define RK3399_PLLCON1_POSTDIV1_SHIFT 8 +#define RK3399_PLLCON1_POSTDIV2_MASK 0x7 +#define RK3399_PLLCON1_POSTDIV2_SHIFT 12 +#define RK3399_PLLCON2_FRAC_MASK 0xffffff +#define RK3399_PLLCON2_FRAC_SHIFT 0 +#define RK3399_PLLCON2_LOCK_STATUS BIT(31) +#define RK3399_PLLCON3_PWRDOWN BIT(0) +#define RK3399_PLLCON3_DSMPD_MASK 0x1 +#define RK3399_PLLCON3_DSMPD_SHIFT 3 + +static int rockchip_rk3399_pll_wait_lock(struct rockchip_clk_pll *pll) +{ + u32 pllcon; + int delay = 24000000; + + /* poll check the lock status in rk3399 xPLLCON2 */ + while (delay > 0) { + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2)); + if (pllcon & RK3399_PLLCON2_LOCK_STATUS) + return 0; + + delay--; + } + + pr_err("%s: timeout waiting for pll to lock\n", __func__); + return -ETIMEDOUT; +} + +static void rockchip_rk3399_pll_get_params(struct rockchip_clk_pll *pll, + struct rockchip_pll_rate_table *rate) +{ + u32 pllcon; + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(0)); + rate->fbdiv = ((pllcon >> RK3399_PLLCON0_FBDIV_SHIFT) + & RK3399_PLLCON0_FBDIV_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(1)); + rate->refdiv = ((pllcon >> RK3399_PLLCON1_REFDIV_SHIFT) + & RK3399_PLLCON1_REFDIV_MASK); + rate->postdiv1 = ((pllcon >> RK3399_PLLCON1_POSTDIV1_SHIFT) + & RK3399_PLLCON1_POSTDIV1_MASK); + rate->postdiv2 = ((pllcon >> RK3399_PLLCON1_POSTDIV2_SHIFT) + & RK3399_PLLCON1_POSTDIV2_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2)); + rate->frac = ((pllcon >> RK3399_PLLCON2_FRAC_SHIFT) + & RK3399_PLLCON2_FRAC_MASK); + + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(3)); + rate->dsmpd = ((pllcon >> RK3399_PLLCON3_DSMPD_SHIFT) + & RK3399_PLLCON3_DSMPD_MASK); +} + +static unsigned long rockchip_rk3399_pll_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + struct rockchip_pll_rate_table cur; + u64 rate64 = prate; + + rockchip_rk3399_pll_get_params(pll, &cur); + + rate64 *= cur.fbdiv; + do_div(rate64, cur.refdiv); + + if (cur.dsmpd == 0) { + /* fractional mode */ + u64 frac_rate64 = prate * cur.frac; + + do_div(frac_rate64, cur.refdiv); + rate64 += frac_rate64 >> 24; + } + + do_div(rate64, cur.postdiv1); + do_div(rate64, cur.postdiv2); + + return (unsigned long)rate64; +} + +static int rockchip_rk3399_pll_set_params(struct rockchip_clk_pll *pll, + const struct rockchip_pll_rate_table *rate) +{ + const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; + struct clk_mux *pll_mux = &pll->pll_mux; + struct rockchip_pll_rate_table cur; + u32 pllcon; + int rate_change_remuxed = 0; + int cur_parent; + int ret; + + pr_debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n", + __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv, + rate->postdiv2, rate->dsmpd, rate->frac); + + rockchip_rk3399_pll_get_params(pll, &cur); + cur.rate = 0; + + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); + if (cur_parent == PLL_MODE_NORM) { + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); + rate_change_remuxed = 1; + } + + /* update pll values */ + writel_relaxed(HIWORD_UPDATE(rate->fbdiv, RK3399_PLLCON0_FBDIV_MASK, + RK3399_PLLCON0_FBDIV_SHIFT), + pll->reg_base + RK3399_PLLCON(0)); + + writel_relaxed(HIWORD_UPDATE(rate->refdiv, RK3399_PLLCON1_REFDIV_MASK, + RK3399_PLLCON1_REFDIV_SHIFT) | + HIWORD_UPDATE(rate->postdiv1, RK3399_PLLCON1_POSTDIV1_MASK, + RK3399_PLLCON1_POSTDIV1_SHIFT) | + HIWORD_UPDATE(rate->postdiv2, RK3399_PLLCON1_POSTDIV2_MASK, + RK3399_PLLCON1_POSTDIV2_SHIFT), + pll->reg_base + RK3399_PLLCON(1)); + + /* xPLL CON2 is not HIWORD_MASK */ + pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2)); + pllcon &= ~(RK3399_PLLCON2_FRAC_MASK << RK3399_PLLCON2_FRAC_SHIFT); + pllcon |= rate->frac << RK3399_PLLCON2_FRAC_SHIFT; + writel_relaxed(pllcon, pll->reg_base + RK3399_PLLCON(2)); + + writel_relaxed(HIWORD_UPDATE(rate->dsmpd, RK3399_PLLCON3_DSMPD_MASK, + RK3399_PLLCON3_DSMPD_SHIFT), + pll->reg_base + RK3399_PLLCON(3)); + + /* wait for the pll to lock */ + ret = rockchip_rk3399_pll_wait_lock(pll); + if (ret) { + pr_warn("%s: pll update unsuccessful, trying to restore old params\n", + __func__); + rockchip_rk3399_pll_set_params(pll, &cur); + } + + if (rate_change_remuxed) + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); + + return ret; +} + +static int rockchip_rk3399_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + const struct rockchip_pll_rate_table *rate; + + pr_debug("%s: changing %s to %lu with a parent rate of %lu\n", + __func__, __clk_get_name(hw->clk), drate, prate); + + /* Get required rate settings from table */ + rate = rockchip_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; + } + + return rockchip_rk3399_pll_set_params(pll, rate); +} + +static int rockchip_rk3399_pll_enable(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + + writel(HIWORD_UPDATE(0, RK3399_PLLCON3_PWRDOWN, 0), + pll->reg_base + RK3399_PLLCON(3)); + + return 0; +} + +static void rockchip_rk3399_pll_disable(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + + writel(HIWORD_UPDATE(RK3399_PLLCON3_PWRDOWN, + RK3399_PLLCON3_PWRDOWN, 0), + pll->reg_base + RK3399_PLLCON(3)); +} + +static int rockchip_rk3399_pll_is_enabled(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + u32 pllcon = readl(pll->reg_base + RK3399_PLLCON(3)); + + return !(pllcon & RK3399_PLLCON3_PWRDOWN); +} + +static void rockchip_rk3399_pll_init(struct clk_hw *hw) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + const struct rockchip_pll_rate_table *rate; + struct rockchip_pll_rate_table cur; + unsigned long drate; + + if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE)) + return; + + drate = clk_hw_get_rate(hw); + rate = rockchip_get_pll_settings(pll, drate); + + /* when no rate setting for the current rate, rely on clk_set_rate */ + if (!rate) + return; + + rockchip_rk3399_pll_get_params(pll, &cur); + + pr_debug("%s: pll %s@%lu: Hz\n", __func__, __clk_get_name(hw->clk), + drate); + pr_debug("old - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n", + cur.fbdiv, cur.postdiv1, cur.refdiv, cur.postdiv2, + cur.dsmpd, cur.frac); + pr_debug("new - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n", + rate->fbdiv, rate->postdiv1, rate->refdiv, rate->postdiv2, + rate->dsmpd, rate->frac); + + if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 || + rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 || + rate->dsmpd != cur.dsmpd || rate->frac != cur.frac) { + struct clk *parent = clk_get_parent(hw->clk); + + if (!parent) { + pr_warn("%s: parent of %s not available\n", + __func__, __clk_get_name(hw->clk)); + return; + } + + pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n", + __func__, __clk_get_name(hw->clk)); + rockchip_rk3399_pll_set_params(pll, rate); + } +} + +static const struct clk_ops rockchip_rk3399_pll_clk_norate_ops = { + .recalc_rate = rockchip_rk3399_pll_recalc_rate, + .enable = rockchip_rk3399_pll_enable, + .disable = rockchip_rk3399_pll_disable, + .is_enabled = rockchip_rk3399_pll_is_enabled, +}; + +static const struct clk_ops rockchip_rk3399_pll_clk_ops = { + .recalc_rate = rockchip_rk3399_pll_recalc_rate, + .round_rate = rockchip_pll_round_rate, + .set_rate = rockchip_rk3399_pll_set_rate, + .enable = rockchip_rk3399_pll_enable, + .disable = rockchip_rk3399_pll_disable, + .is_enabled = rockchip_rk3399_pll_is_enabled, + .init = rockchip_rk3399_pll_init, +}; + /* * Common registering of pll clocks */ -struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, +struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, + enum rockchip_pll_type pll_type, const char *name, const char *const *parent_names, - u8 num_parents, void __iomem *base, int con_offset, - int grf_lock_offset, int lock_shift, int mode_offset, - int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags, spinlock_t *lock) + u8 num_parents, int con_offset, int grf_lock_offset, + int lock_shift, int mode_offset, int mode_shift, + struct rockchip_pll_rate_table *rate_table, + u8 clk_pll_flags) { const char *pll_parents[3]; struct clk_init_data init; @@ -624,14 +861,16 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, /* create the mux on top of the real pll */ pll->pll_mux_ops = &clk_mux_ops; pll_mux = &pll->pll_mux; - pll_mux->reg = base + mode_offset; + pll_mux->reg = ctx->reg_base + mode_offset; pll_mux->shift = mode_shift; pll_mux->mask = PLL_MODE_MASK; pll_mux->flags = 0; - pll_mux->lock = lock; + pll_mux->lock = &ctx->lock; pll_mux->hw.init = &init; - if (pll_type == pll_rk3036 || pll_type == pll_rk3066) + if (pll_type == pll_rk3036 || + pll_type == pll_rk3066 || + pll_type == pll_rk3399) pll_mux->flags |= CLK_MUX_HIWORD_MASK; /* the actual muxing is xin24m, pll-output, xin32k */ @@ -677,17 +916,23 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, switch (pll_type) { case pll_rk3036: - if (!pll->rate_table) + if (!pll->rate_table || IS_ERR(ctx->grf)) init.ops = &rockchip_rk3036_pll_clk_norate_ops; else init.ops = &rockchip_rk3036_pll_clk_ops; break; case pll_rk3066: - if (!pll->rate_table) + if (!pll->rate_table || IS_ERR(ctx->grf)) init.ops = &rockchip_rk3066_pll_clk_norate_ops; else init.ops = &rockchip_rk3066_pll_clk_ops; break; + case pll_rk3399: + if (!pll->rate_table) + init.ops = &rockchip_rk3399_pll_clk_norate_ops; + else + init.ops = &rockchip_rk3399_pll_clk_ops; + break; default: pr_warn("%s: Unknown pll type for pll clk %s\n", __func__, name); @@ -695,11 +940,12 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, pll->hw.init = &init; pll->type = pll_type; - pll->reg_base = base + con_offset; + pll->reg_base = ctx->reg_base + con_offset; pll->lock_offset = grf_lock_offset; pll->lock_shift = lock_shift; pll->flags = clk_pll_flags; - pll->lock = lock; + pll->lock = &ctx->lock; + pll->ctx = ctx; pll_clk = clk_register(NULL, &pll->hw); if (IS_ERR(pll_clk)) { diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c index 7cdb2d61f3e0..924f560dcf80 100644 --- a/drivers/clk/rockchip/clk-rk3036.c +++ b/drivers/clk/rockchip/clk-rk3036.c @@ -113,7 +113,10 @@ static const struct rockchip_cpuclk_reg_data rk3036_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 7, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "xin24m", "xin24m" }; @@ -437,6 +440,7 @@ static const char *const rk3036_critical_clocks[] __initconst = { static void __init rk3036_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; struct clk *clk; @@ -446,22 +450,27 @@ static void __init rk3036_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1); if (IS_ERR(clk)) pr_warn("%s: could not register clock usb480m: %ld\n", __func__, PTR_ERR(clk)); - rockchip_clk_register_plls(rk3036_pll_clks, + rockchip_clk_register_plls(ctx, rk3036_pll_clks, ARRAY_SIZE(rk3036_pll_clks), RK3036_GRF_SOC_STATUS0); - rockchip_clk_register_branches(rk3036_clk_branches, + rockchip_clk_register_branches(ctx, rk3036_clk_branches, ARRAY_SIZE(rk3036_clk_branches)); rockchip_clk_protect_critical(rk3036_critical_clocks, ARRAY_SIZE(rk3036_critical_clocks)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3036_cpuclk_data, rk3036_cpuclk_rates, ARRAY_SIZE(rk3036_cpuclk_rates)); @@ -469,6 +478,8 @@ static void __init rk3036_clk_init(struct device_node *np) rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 40bab3901491..d0e722a0e8cf 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -155,7 +155,10 @@ static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 8, + .mux_core_mask = 0x1, }; #define RK3188_DIV_ACLK_CORE_MASK 0x7 @@ -191,7 +194,10 @@ static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 9, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 8, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "xin24m", "xin32k" }; @@ -753,57 +759,75 @@ static const char *const rk3188_critical_clocks[] __initconst = { "hclk_cpubus" }; -static void __init rk3188_common_clk_init(struct device_node *np) +static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; reg_base = of_iomap(np, 0); if (!reg_base) { pr_err("%s: could not map cru region\n", __func__); - return; + return ERR_PTR(-ENOMEM); } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return ERR_PTR(-ENOMEM); + } - rockchip_clk_register_branches(common_clk_branches, + rockchip_clk_register_branches(ctx, common_clk_branches, ARRAY_SIZE(common_clk_branches)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL); + + return ctx; } static void __init rk3066a_clk_init(struct device_node *np) { - rk3188_common_clk_init(np); - rockchip_clk_register_plls(rk3066_pll_clks, + struct rockchip_clk_provider *ctx; + + ctx = rk3188_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_plls(ctx, rk3066_pll_clks, ARRAY_SIZE(rk3066_pll_clks), RK3066_GRF_SOC_STATUS); - rockchip_clk_register_branches(rk3066a_clk_branches, + rockchip_clk_register_branches(ctx, rk3066a_clk_branches, ARRAY_SIZE(rk3066a_clk_branches)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3066_cpuclk_data, rk3066_cpuclk_rates, ARRAY_SIZE(rk3066_cpuclk_rates)); rockchip_clk_protect_critical(rk3188_critical_clocks, ARRAY_SIZE(rk3188_critical_clocks)); + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); static void __init rk3188a_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; struct clk *clk1, *clk2; unsigned long rate; int ret; - rk3188_common_clk_init(np); - rockchip_clk_register_plls(rk3188_pll_clks, + ctx = rk3188_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_plls(ctx, rk3188_pll_clks, ARRAY_SIZE(rk3188_pll_clks), RK3188_GRF_SOC_STATUS); - rockchip_clk_register_branches(rk3188_clk_branches, + rockchip_clk_register_branches(ctx, rk3188_clk_branches, ARRAY_SIZE(rk3188_clk_branches)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3188_cpuclk_data, rk3188_cpuclk_rates, ARRAY_SIZE(rk3188_cpuclk_rates)); @@ -827,6 +851,7 @@ static void __init rk3188a_clk_init(struct device_node *np) rockchip_clk_protect_critical(rk3188_critical_clocks, ARRAY_SIZE(rk3188_critical_clocks)); + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c index 7702d2855e9c..016bdb0b793a 100644 --- a/drivers/clk/rockchip/clk-rk3228.c +++ b/drivers/clk/rockchip/clk-rk3228.c @@ -111,7 +111,10 @@ static const struct rockchip_cpuclk_reg_data rk3228_cpuclk_data = { .core_reg = RK2928_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 6, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "clk_24m", "xin24m" }; @@ -625,6 +628,7 @@ static const char *const rk3228_critical_clocks[] __initconst = { static void __init rk3228_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; reg_base = of_iomap(np, 0); @@ -633,17 +637,22 @@ static void __init rk3228_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } - rockchip_clk_register_plls(rk3228_pll_clks, + rockchip_clk_register_plls(ctx, rk3228_pll_clks, ARRAY_SIZE(rk3228_pll_clks), RK3228_GRF_SOC_STATUS0); - rockchip_clk_register_branches(rk3228_clk_branches, + rockchip_clk_register_branches(ctx, rk3228_clk_branches, ARRAY_SIZE(rk3228_clk_branches)); rockchip_clk_protect_critical(rk3228_critical_clocks, ARRAY_SIZE(rk3228_critical_clocks)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3228_cpuclk_data, rk3228_cpuclk_rates, ARRAY_SIZE(rk3228_cpuclk_rates)); @@ -651,6 +660,8 @@ static void __init rk3228_clk_init(struct device_node *np) rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK3228_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK3228_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 3cb72163a512..39af05a589b3 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -165,7 +165,10 @@ static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = { .core_reg = RK3288_CLKSEL_CON(0), .div_core_shift = 8, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 15, + .mux_core_mask = 0x1, }; PNAME(mux_pll_p) = { "xin24m", "xin32k" }; @@ -878,6 +881,7 @@ static struct syscore_ops rk3288_clk_syscore_ops = { static void __init rk3288_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; struct clk *clk; rk3288_cru_base = of_iomap(np, 0); @@ -886,7 +890,12 @@ static void __init rk3288_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(rk3288_cru_base); + return; + } /* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */ clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1); @@ -894,17 +903,17 @@ static void __init rk3288_clk_init(struct device_node *np) pr_warn("%s: could not register clock pclk_wdt: %ld\n", __func__, PTR_ERR(clk)); else - rockchip_clk_add_lookup(clk, PCLK_WDT); + rockchip_clk_add_lookup(ctx, clk, PCLK_WDT); - rockchip_clk_register_plls(rk3288_pll_clks, + rockchip_clk_register_plls(ctx, rk3288_pll_clks, ARRAY_SIZE(rk3288_pll_clks), RK3288_GRF_SOC_STATUS1); - rockchip_clk_register_branches(rk3288_clk_branches, + rockchip_clk_register_branches(ctx, rk3288_clk_branches, ARRAY_SIZE(rk3288_clk_branches)); rockchip_clk_protect_critical(rk3288_critical_clocks, ARRAY_SIZE(rk3288_critical_clocks)); - rockchip_clk_register_armclk(ARMCLK, "armclk", + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3288_cpuclk_data, rk3288_cpuclk_rates, ARRAY_SIZE(rk3288_cpuclk_rates)); @@ -913,8 +922,10 @@ static void __init rk3288_clk_init(struct device_node *np) rk3288_cru_base + RK3288_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK3288_GLB_SRST_FST, + rockchip_register_restart_notifier(ctx, RK3288_GLB_SRST_FST, rk3288_clk_shutdown); register_syscore_ops(&rk3288_clk_syscore_ops); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c index a2bb12200465..6cb474c593e7 100644 --- a/drivers/clk/rockchip/clk-rk3368.c +++ b/drivers/clk/rockchip/clk-rk3368.c @@ -165,14 +165,20 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkb_data = { .core_reg = RK3368_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, .mux_core_shift = 7, + .mux_core_mask = 0x1, }; static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = { .core_reg = RK3368_CLKSEL_CON(2), .div_core_shift = 0, + .mux_core_alt = 1, + .mux_core_main = 0, .div_core_mask = 0x1f, .mux_core_shift = 7, + .mux_core_mask = 0x1, }; #define RK3368_DIV_ACLKM_MASK 0x1f @@ -856,6 +862,7 @@ static const char *const rk3368_critical_clocks[] __initconst = { static void __init rk3368_clk_init(struct device_node *np) { + struct rockchip_clk_provider *ctx; void __iomem *reg_base; struct clk *clk; @@ -865,7 +872,12 @@ static void __init rk3368_clk_init(struct device_node *np) return; } - rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } /* Watchdog pclk is controlled by sgrf_soc_con3[7]. */ clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1); @@ -873,22 +885,22 @@ static void __init rk3368_clk_init(struct device_node *np) pr_warn("%s: could not register clock pclk_wdt: %ld\n", __func__, PTR_ERR(clk)); else - rockchip_clk_add_lookup(clk, PCLK_WDT); + rockchip_clk_add_lookup(ctx, clk, PCLK_WDT); - rockchip_clk_register_plls(rk3368_pll_clks, + rockchip_clk_register_plls(ctx, rk3368_pll_clks, ARRAY_SIZE(rk3368_pll_clks), RK3368_GRF_SOC_STATUS0); - rockchip_clk_register_branches(rk3368_clk_branches, + rockchip_clk_register_branches(ctx, rk3368_clk_branches, ARRAY_SIZE(rk3368_clk_branches)); rockchip_clk_protect_critical(rk3368_critical_clocks, ARRAY_SIZE(rk3368_critical_clocks)); - rockchip_clk_register_armclk(ARMCLKB, "armclkb", + rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb", mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p), &rk3368_cpuclkb_data, rk3368_cpuclkb_rates, ARRAY_SIZE(rk3368_cpuclkb_rates)); - rockchip_clk_register_armclk(ARMCLKL, "armclkl", + rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl", mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p), &rk3368_cpuclkl_data, rk3368_cpuclkl_rates, ARRAY_SIZE(rk3368_cpuclkl_rates)); @@ -896,6 +908,8 @@ static void __init rk3368_clk_init(struct device_node *np) rockchip_register_softrst(np, 15, reg_base + RK3368_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); - rockchip_register_restart_notifier(RK3368_GLB_SRST_FST, NULL); + rockchip_register_restart_notifier(ctx, RK3368_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(rk3368_cru, "rockchip,rk3368-cru", rk3368_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c new file mode 100644 index 000000000000..291543f52caa --- /dev/null +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -0,0 +1,1573 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Xing Zheng <zhengxing@rock-chips.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <dt-bindings/clock/rk3399-cru.h> +#include "clk.h" + +enum rk3399_plls { + lpll, bpll, dpll, cpll, gpll, npll, vpll, +}; + +enum rk3399_pmu_plls { + ppll, +}; + +static struct rockchip_pll_rate_table rk3399_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(2208000000, 1, 92, 1, 1, 1, 0), + RK3036_PLL_RATE(2184000000, 1, 91, 1, 1, 1, 0), + RK3036_PLL_RATE(2160000000, 1, 90, 1, 1, 1, 0), + RK3036_PLL_RATE(2136000000, 1, 89, 1, 1, 1, 0), + RK3036_PLL_RATE(2112000000, 1, 88, 1, 1, 1, 0), + RK3036_PLL_RATE(2088000000, 1, 87, 1, 1, 1, 0), + RK3036_PLL_RATE(2064000000, 1, 86, 1, 1, 1, 0), + RK3036_PLL_RATE(2040000000, 1, 85, 1, 1, 1, 0), + RK3036_PLL_RATE(2016000000, 1, 84, 1, 1, 1, 0), + RK3036_PLL_RATE(1992000000, 1, 83, 1, 1, 1, 0), + RK3036_PLL_RATE(1968000000, 1, 82, 1, 1, 1, 0), + RK3036_PLL_RATE(1944000000, 1, 81, 1, 1, 1, 0), + RK3036_PLL_RATE(1920000000, 1, 80, 1, 1, 1, 0), + RK3036_PLL_RATE(1896000000, 1, 79, 1, 1, 1, 0), + RK3036_PLL_RATE(1872000000, 1, 78, 1, 1, 1, 0), + RK3036_PLL_RATE(1848000000, 1, 77, 1, 1, 1, 0), + RK3036_PLL_RATE(1824000000, 1, 76, 1, 1, 1, 0), + RK3036_PLL_RATE(1800000000, 1, 75, 1, 1, 1, 0), + RK3036_PLL_RATE(1776000000, 1, 74, 1, 1, 1, 0), + RK3036_PLL_RATE(1752000000, 1, 73, 1, 1, 1, 0), + RK3036_PLL_RATE(1728000000, 1, 72, 1, 1, 1, 0), + RK3036_PLL_RATE(1704000000, 1, 71, 1, 1, 1, 0), + RK3036_PLL_RATE(1680000000, 1, 70, 1, 1, 1, 0), + RK3036_PLL_RATE(1656000000, 1, 69, 1, 1, 1, 0), + RK3036_PLL_RATE(1632000000, 1, 68, 1, 1, 1, 0), + RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0), + RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0), + RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0), + RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0), + RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0), + RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0), + RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0), + RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0), + RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0), + RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0), + RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0), + RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0), + RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0), + RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0), + RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), + RK3036_PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0), + RK3036_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), + RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + RK3036_PLL_RATE( 984000000, 1, 82, 2, 1, 1, 0), + RK3036_PLL_RATE( 960000000, 1, 80, 2, 1, 1, 0), + RK3036_PLL_RATE( 936000000, 1, 78, 2, 1, 1, 0), + RK3036_PLL_RATE( 912000000, 1, 76, 2, 1, 1, 0), + RK3036_PLL_RATE( 900000000, 4, 300, 2, 1, 1, 0), + RK3036_PLL_RATE( 888000000, 1, 74, 2, 1, 1, 0), + RK3036_PLL_RATE( 864000000, 1, 72, 2, 1, 1, 0), + RK3036_PLL_RATE( 840000000, 1, 70, 2, 1, 1, 0), + RK3036_PLL_RATE( 816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE( 800000000, 6, 400, 2, 1, 1, 0), + RK3036_PLL_RATE( 700000000, 6, 350, 2, 1, 1, 0), + RK3036_PLL_RATE( 696000000, 1, 58, 2, 1, 1, 0), + RK3036_PLL_RATE( 676000000, 3, 169, 2, 1, 1, 0), + RK3036_PLL_RATE( 600000000, 1, 75, 3, 1, 1, 0), + RK3036_PLL_RATE( 594000000, 1, 99, 4, 1, 1, 0), + RK3036_PLL_RATE( 504000000, 1, 63, 3, 1, 1, 0), + RK3036_PLL_RATE( 500000000, 6, 250, 2, 1, 1, 0), + RK3036_PLL_RATE( 408000000, 1, 68, 2, 2, 1, 0), + RK3036_PLL_RATE( 312000000, 1, 52, 2, 2, 1, 0), + RK3036_PLL_RATE( 297000000, 1, 99, 4, 2, 1, 0), + RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0), + RK3036_PLL_RATE( 148500000, 1, 99, 4, 4, 1, 0), + RK3036_PLL_RATE( 96000000, 1, 64, 4, 4, 1, 0), + RK3036_PLL_RATE( 74250000, 2, 99, 4, 4, 1, 0), + RK3036_PLL_RATE( 54000000, 1, 54, 6, 4, 1, 0), + RK3036_PLL_RATE( 27000000, 1, 27, 6, 4, 1, 0), + { /* sentinel */ }, +}; + +/* CRU parents */ +PNAME(mux_pll_p) = { "xin24m", "xin32k" }; + +PNAME(mux_armclkl_p) = { "clk_core_l_lpll_src", + "clk_core_l_bpll_src", + "clk_core_l_dpll_src", + "clk_core_l_gpll_src" }; +PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src", + "clk_core_b_bpll_src", + "clk_core_b_dpll_src", + "clk_core_b_gpll_src" }; +PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src", + "gpll_aclk_cci_src", + "npll_aclk_cci_src", + "vpll_aclk_cci_src" }; +PNAME(mux_cci_trace_p) = { "cpll_cci_trace", + "gpll_cci_trace" }; +PNAME(mux_cs_p) = { "cpll_cs", "gpll_cs", + "npll_cs"}; +PNAME(mux_aclk_perihp_p) = { "cpll_aclk_perihp_src", + "gpll_aclk_perihp_src" }; + +PNAME(mux_pll_src_cpll_gpll_p) = { "cpll", "gpll" }; +PNAME(mux_pll_src_cpll_gpll_npll_p) = { "cpll", "gpll", "npll" }; +PNAME(mux_pll_src_cpll_gpll_ppll_p) = { "cpll", "gpll", "ppll" }; +PNAME(mux_pll_src_cpll_gpll_upll_p) = { "cpll", "gpll", "upll" }; +PNAME(mux_pll_src_npll_cpll_gpll_p) = { "npll", "cpll", "gpll" }; +PNAME(mux_pll_src_cpll_gpll_npll_ppll_p) = { "cpll", "gpll", "npll", + "ppll" }; +PNAME(mux_pll_src_cpll_gpll_npll_24m_p) = { "cpll", "gpll", "npll", + "xin24m" }; +PNAME(mux_pll_src_cpll_gpll_npll_usbphy480m_p) = { "cpll", "gpll", "npll", + "clk_usbphy_480m" }; +PNAME(mux_pll_src_ppll_cpll_gpll_npll_p) = { "ppll", "cpll", "gpll", + "npll", "upll" }; +PNAME(mux_pll_src_cpll_gpll_npll_upll_24m_p) = { "cpll", "gpll", "npll", + "upll", "xin24m" }; +PNAME(mux_pll_src_cpll_gpll_npll_ppll_upll_24m_p) = { "cpll", "gpll", "npll", + "ppll", "upll", "xin24m" }; + +PNAME(mux_pll_src_vpll_cpll_gpll_p) = { "vpll", "cpll", "gpll" }; +PNAME(mux_pll_src_vpll_cpll_gpll_npll_p) = { "vpll", "cpll", "gpll", + "npll" }; +PNAME(mux_pll_src_vpll_cpll_gpll_24m_p) = { "vpll", "cpll", "gpll", + "xin24m" }; + +PNAME(mux_dclk_vop0_p) = { "dclk_vop0_div", + "dclk_vop0_frac" }; +PNAME(mux_dclk_vop1_p) = { "dclk_vop1_div", + "dclk_vop1_frac" }; + +PNAME(mux_clk_cif_p) = { "clk_cifout_src", "xin24m" }; + +PNAME(mux_pll_src_24m_usbphy480m_p) = { "xin24m", "clk_usbphy_480m" }; +PNAME(mux_pll_src_24m_pciephy_p) = { "xin24m", "clk_pciephy_ref100m" }; +PNAME(mux_pll_src_24m_32k_cpll_gpll_p) = { "xin24m", "xin32k", + "cpll", "gpll" }; +PNAME(mux_pciecore_cru_phy_p) = { "clk_pcie_core_cru", + "clk_pcie_core_phy" }; + +PNAME(mux_aclk_emmc_p) = { "cpll_aclk_emmc_src", + "gpll_aclk_emmc_src" }; + +PNAME(mux_aclk_perilp0_p) = { "cpll_aclk_perilp0_src", + "gpll_aclk_perilp0_src" }; + +PNAME(mux_fclk_cm0s_p) = { "cpll_fclk_cm0s_src", + "gpll_fclk_cm0s_src" }; + +PNAME(mux_hclk_perilp1_p) = { "cpll_hclk_perilp1_src", + "gpll_hclk_perilp1_src" }; + +PNAME(mux_clk_testout1_p) = { "clk_testout1_pll_src", "xin24m" }; +PNAME(mux_clk_testout2_p) = { "clk_testout2_pll_src", "xin24m" }; + +PNAME(mux_usbphy_480m_p) = { "clk_usbphy0_480m_src", + "clk_usbphy1_480m_src" }; +PNAME(mux_aclk_gmac_p) = { "cpll_aclk_gmac_src", + "gpll_aclk_gmac_src" }; +PNAME(mux_rmii_p) = { "clk_gmac", "clkin_gmac" }; +PNAME(mux_spdif_p) = { "clk_spdif_div", "clk_spdif_frac", + "clkin_i2s", "xin12m" }; +PNAME(mux_i2s0_p) = { "clk_i2s0_div", "clk_i2s0_frac", + "clkin_i2s", "xin12m" }; +PNAME(mux_i2s1_p) = { "clk_i2s1_div", "clk_i2s1_frac", + "clkin_i2s", "xin12m" }; +PNAME(mux_i2s2_p) = { "clk_i2s2_div", "clk_i2s2_frac", + "clkin_i2s", "xin12m" }; +PNAME(mux_i2sch_p) = { "clk_i2s0", "clk_i2s1", + "clk_i2s2" }; +PNAME(mux_i2sout_p) = { "clk_i2sout_src", "xin12m" }; + +PNAME(mux_uart0_p) = { "clk_uart0_div", "clk_uart0_frac", "xin24m" }; +PNAME(mux_uart1_p) = { "clk_uart1_div", "clk_uart1_frac", "xin24m" }; +PNAME(mux_uart2_p) = { "clk_uart2_div", "clk_uart2_frac", "xin24m" }; +PNAME(mux_uart3_p) = { "clk_uart3_div", "clk_uart3_frac", "xin24m" }; + +/* PMU CRU parents */ +PNAME(mux_ppll_24m_p) = { "ppll", "xin24m" }; +PNAME(mux_24m_ppll_p) = { "xin24m", "ppll" }; +PNAME(mux_fclk_cm0s_pmu_ppll_p) = { "fclk_cm0s_pmu_ppll_src", "xin24m" }; +PNAME(mux_wifi_pmu_p) = { "clk_wifi_div", "clk_wifi_frac" }; +PNAME(mux_uart4_pmu_p) = { "clk_uart4_div", "clk_uart4_frac", + "xin24m" }; +PNAME(mux_clk_testout2_2io_p) = { "clk_testout2", "clk_32k_suspend_pmu" }; + +static struct rockchip_pll_clock rk3399_pll_clks[] __initdata = { + [lpll] = PLL(pll_rk3399, PLL_APLLL, "lpll", mux_pll_p, 0, RK3399_PLL_CON(0), + RK3399_PLL_CON(3), 8, 31, 0, rk3399_pll_rates), + [bpll] = PLL(pll_rk3399, PLL_APLLB, "bpll", mux_pll_p, 0, RK3399_PLL_CON(8), + RK3399_PLL_CON(11), 8, 31, 0, rk3399_pll_rates), + [dpll] = PLL(pll_rk3399, PLL_DPLL, "dpll", mux_pll_p, 0, RK3399_PLL_CON(16), + RK3399_PLL_CON(19), 8, 31, 0, NULL), + [cpll] = PLL(pll_rk3399, PLL_CPLL, "cpll", mux_pll_p, 0, RK3399_PLL_CON(24), + RK3399_PLL_CON(27), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), + [gpll] = PLL(pll_rk3399, PLL_GPLL, "gpll", mux_pll_p, 0, RK3399_PLL_CON(32), + RK3399_PLL_CON(35), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), + [npll] = PLL(pll_rk3399, PLL_NPLL, "npll", mux_pll_p, 0, RK3399_PLL_CON(40), + RK3399_PLL_CON(43), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), + [vpll] = PLL(pll_rk3399, PLL_VPLL, "vpll", mux_pll_p, 0, RK3399_PLL_CON(48), + RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), +}; + +static struct rockchip_pll_clock rk3399_pmu_pll_clks[] __initdata = { + [ppll] = PLL(pll_rk3399, PLL_PPLL, "ppll", mux_pll_p, 0, RK3399_PMU_PLL_CON(0), + RK3399_PMU_PLL_CON(3), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) +#define IFLAGS ROCKCHIP_INVERTER_HIWORD_MASK + +static struct rockchip_clk_branch rk3399_spdif_fracmux __initdata = + MUX(0, "clk_spdif_mux", mux_spdif_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(32), 13, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_i2s0_fracmux __initdata = + MUX(0, "clk_i2s0_mux", mux_i2s0_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(28), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_i2s1_fracmux __initdata = + MUX(0, "clk_i2s1_mux", mux_i2s1_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(29), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_i2s2_fracmux __initdata = + MUX(0, "clk_i2s2_mux", mux_i2s2_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(30), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_uart0_fracmux __initdata = + MUX(SCLK_UART0, "clk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(33), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_uart1_fracmux __initdata = + MUX(SCLK_UART1, "clk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(34), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_uart2_fracmux __initdata = + MUX(SCLK_UART2, "clk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(35), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_uart3_fracmux __initdata = + MUX(SCLK_UART3, "clk_uart3", mux_uart3_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(36), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_uart4_pmu_fracmux __initdata = + MUX(SCLK_UART4_PMU, "clk_uart4_pmu", mux_uart4_pmu_p, CLK_SET_RATE_PARENT, + RK3399_PMU_CLKSEL_CON(5), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3399_dclk_vop0_fracmux __initdata = + MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(49), 11, 1, MFLAGS); + +static struct rockchip_clk_branch rk3399_dclk_vop1_fracmux __initdata = + MUX(DCLK_VOP1, "dclk_vop1", mux_dclk_vop1_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(50), 11, 1, MFLAGS); + +static struct rockchip_clk_branch rk3399_pmuclk_wifi_fracmux __initdata = + MUX(SCLK_WIFI_PMU, "clk_wifi_pmu", mux_wifi_pmu_p, CLK_SET_RATE_PARENT, + RK3399_PMU_CLKSEL_CON(1), 14, 1, MFLAGS); + +static const struct rockchip_cpuclk_reg_data rk3399_cpuclkl_data = { + .core_reg = RK3399_CLKSEL_CON(0), + .div_core_shift = 0, + .div_core_mask = 0x1f, + .mux_core_alt = 3, + .mux_core_main = 0, + .mux_core_shift = 6, + .mux_core_mask = 0x3, +}; + +static const struct rockchip_cpuclk_reg_data rk3399_cpuclkb_data = { + .core_reg = RK3399_CLKSEL_CON(2), + .div_core_shift = 0, + .div_core_mask = 0x1f, + .mux_core_alt = 3, + .mux_core_main = 1, + .mux_core_shift = 6, + .mux_core_mask = 0x3, +}; + +#define RK3399_DIV_ACLKM_MASK 0x1f +#define RK3399_DIV_ACLKM_SHIFT 8 +#define RK3399_DIV_ATCLK_MASK 0x1f +#define RK3399_DIV_ATCLK_SHIFT 0 +#define RK3399_DIV_PCLK_DBG_MASK 0x1f +#define RK3399_DIV_PCLK_DBG_SHIFT 8 + +#define RK3399_CLKSEL0(_offs, _aclkm) \ + { \ + .reg = RK3399_CLKSEL_CON(0 + _offs), \ + .val = HIWORD_UPDATE(_aclkm, RK3399_DIV_ACLKM_MASK, \ + RK3399_DIV_ACLKM_SHIFT), \ + } +#define RK3399_CLKSEL1(_offs, _atclk, _pdbg) \ + { \ + .reg = RK3399_CLKSEL_CON(1 + _offs), \ + .val = HIWORD_UPDATE(_atclk, RK3399_DIV_ATCLK_MASK, \ + RK3399_DIV_ATCLK_SHIFT) | \ + HIWORD_UPDATE(_pdbg, RK3399_DIV_PCLK_DBG_MASK, \ + RK3399_DIV_PCLK_DBG_SHIFT), \ + } + +/* cluster_l: aclkm in clksel0, rest in clksel1 */ +#define RK3399_CPUCLKL_RATE(_prate, _aclkm, _atclk, _pdbg) \ + { \ + .prate = _prate##U, \ + .divs = { \ + RK3399_CLKSEL0(0, _aclkm), \ + RK3399_CLKSEL1(0, _atclk, _pdbg), \ + }, \ + } + +/* cluster_b: aclkm in clksel2, rest in clksel3 */ +#define RK3399_CPUCLKB_RATE(_prate, _aclkm, _atclk, _pdbg) \ + { \ + .prate = _prate##U, \ + .divs = { \ + RK3399_CLKSEL0(2, _aclkm), \ + RK3399_CLKSEL1(2, _atclk, _pdbg), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3399_cpuclkl_rates[] __initdata = { + RK3399_CPUCLKL_RATE(1800000000, 1, 8, 8), + RK3399_CPUCLKL_RATE(1704000000, 1, 8, 8), + RK3399_CPUCLKL_RATE(1608000000, 1, 7, 7), + RK3399_CPUCLKL_RATE(1512000000, 1, 7, 7), + RK3399_CPUCLKL_RATE(1488000000, 1, 6, 6), + RK3399_CPUCLKL_RATE(1416000000, 1, 6, 6), + RK3399_CPUCLKL_RATE(1200000000, 1, 5, 5), + RK3399_CPUCLKL_RATE(1008000000, 1, 5, 5), + RK3399_CPUCLKL_RATE( 816000000, 1, 4, 4), + RK3399_CPUCLKL_RATE( 696000000, 1, 3, 3), + RK3399_CPUCLKL_RATE( 600000000, 1, 3, 3), + RK3399_CPUCLKL_RATE( 408000000, 1, 2, 2), + RK3399_CPUCLKL_RATE( 312000000, 1, 1, 1), + RK3399_CPUCLKL_RATE( 216000000, 1, 1, 1), + RK3399_CPUCLKL_RATE( 96000000, 1, 1, 1), +}; + +static struct rockchip_cpuclk_rate_table rk3399_cpuclkb_rates[] __initdata = { + RK3399_CPUCLKB_RATE(2208000000, 1, 11, 11), + RK3399_CPUCLKB_RATE(2184000000, 1, 11, 11), + RK3399_CPUCLKB_RATE(2088000000, 1, 10, 10), + RK3399_CPUCLKB_RATE(2040000000, 1, 10, 10), + RK3399_CPUCLKB_RATE(1992000000, 1, 9, 9), + RK3399_CPUCLKB_RATE(1896000000, 1, 9, 9), + RK3399_CPUCLKB_RATE(1800000000, 1, 8, 8), + RK3399_CPUCLKB_RATE(1704000000, 1, 8, 8), + RK3399_CPUCLKB_RATE(1608000000, 1, 7, 7), + RK3399_CPUCLKB_RATE(1512000000, 1, 7, 7), + RK3399_CPUCLKB_RATE(1488000000, 1, 6, 6), + RK3399_CPUCLKB_RATE(1416000000, 1, 6, 6), + RK3399_CPUCLKB_RATE(1200000000, 1, 5, 5), + RK3399_CPUCLKB_RATE(1008000000, 1, 5, 5), + RK3399_CPUCLKB_RATE( 816000000, 1, 4, 4), + RK3399_CPUCLKB_RATE( 696000000, 1, 3, 3), + RK3399_CPUCLKB_RATE( 600000000, 1, 3, 3), + RK3399_CPUCLKB_RATE( 408000000, 1, 2, 2), + RK3399_CPUCLKB_RATE( 312000000, 1, 1, 1), + RK3399_CPUCLKB_RATE( 216000000, 1, 1, 1), + RK3399_CPUCLKB_RATE( 96000000, 1, 1, 1), +}; + +static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { + /* + * CRU Clock-Architecture + */ + + /* usbphy */ + GATE(SCLK_USB2PHY0_REF, "clk_usb2phy0_ref", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(6), 5, GFLAGS), + GATE(SCLK_USB2PHY1_REF, "clk_usb2phy1_ref", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(6), 6, GFLAGS), + + GATE(0, "clk_usbphy0_480m_src", "clk_usbphy0_480m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(13), 12, GFLAGS), + GATE(0, "clk_usbphy1_480m_src", "clk_usbphy1_480m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(13), 12, GFLAGS), + MUX(0, "clk_usbphy_480m", mux_usbphy_480m_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(14), 6, 1, MFLAGS), + + MUX(0, "upll", mux_pll_src_24m_usbphy480m_p, 0, + RK3399_CLKSEL_CON(14), 15, 1, MFLAGS), + + COMPOSITE_NODIV(SCLK_HSICPHY, "clk_hsicphy", mux_pll_src_cpll_gpll_npll_usbphy480m_p, 0, + RK3399_CLKSEL_CON(19), 0, 2, MFLAGS, + RK3399_CLKGATE_CON(6), 4, GFLAGS), + + COMPOSITE(ACLK_USB3, "aclk_usb3", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 0, GFLAGS), + GATE(ACLK_USB3_NOC, "aclk_usb3_noc", "aclk_usb3", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(30), 0, GFLAGS), + GATE(ACLK_USB3OTG0, "aclk_usb3otg0", "aclk_usb3", 0, + RK3399_CLKGATE_CON(30), 1, GFLAGS), + GATE(ACLK_USB3OTG1, "aclk_usb3otg1", "aclk_usb3", 0, + RK3399_CLKGATE_CON(30), 2, GFLAGS), + GATE(ACLK_USB3_RKSOC_AXI_PERF, "aclk_usb3_rksoc_axi_perf", "aclk_usb3", 0, + RK3399_CLKGATE_CON(30), 3, GFLAGS), + GATE(ACLK_USB3_GRF, "aclk_usb3_grf", "aclk_usb3", 0, + RK3399_CLKGATE_CON(30), 4, GFLAGS), + + GATE(SCLK_USB3OTG0_REF, "clk_usb3otg0_ref", "xin24m", 0, + RK3399_CLKGATE_CON(12), 1, GFLAGS), + GATE(SCLK_USB3OTG1_REF, "clk_usb3otg1_ref", "xin24m", 0, + RK3399_CLKGATE_CON(12), 2, GFLAGS), + + COMPOSITE(SCLK_USB3OTG0_SUSPEND, "clk_usb3otg0_suspend", mux_pll_p, 0, + RK3399_CLKSEL_CON(40), 15, 1, MFLAGS, 0, 10, DFLAGS, + RK3399_CLKGATE_CON(12), 3, GFLAGS), + + COMPOSITE(SCLK_USB3OTG1_SUSPEND, "clk_usb3otg1_suspend", mux_pll_p, 0, + RK3399_CLKSEL_CON(41), 15, 1, MFLAGS, 0, 10, DFLAGS, + RK3399_CLKGATE_CON(12), 4, GFLAGS), + + COMPOSITE(SCLK_UPHY0_TCPDPHY_REF, "clk_uphy0_tcpdphy_ref", mux_pll_p, 0, + RK3399_CLKSEL_CON(64), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 4, GFLAGS), + + COMPOSITE(SCLK_UPHY0_TCPDCORE, "clk_uphy0_tcpdcore", mux_pll_src_24m_32k_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(64), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 5, GFLAGS), + + COMPOSITE(SCLK_UPHY1_TCPDPHY_REF, "clk_uphy1_tcpdphy_ref", mux_pll_p, 0, + RK3399_CLKSEL_CON(65), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 6, GFLAGS), + + COMPOSITE(SCLK_UPHY1_TCPDCORE, "clk_uphy1_tcpdcore", mux_pll_src_24m_32k_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(65), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 7, GFLAGS), + + /* little core */ + GATE(0, "clk_core_l_lpll_src", "lpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(0), 0, GFLAGS), + GATE(0, "clk_core_l_bpll_src", "bpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(0), 1, GFLAGS), + GATE(0, "clk_core_l_dpll_src", "dpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(0), 2, GFLAGS), + GATE(0, "clk_core_l_gpll_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(0), 3, GFLAGS), + + COMPOSITE_NOMUX(0, "aclkm_core_l", "armclkl", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(0), 8, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3399_CLKGATE_CON(0), 4, GFLAGS), + COMPOSITE_NOMUX(0, "atclk_core_l", "armclkl", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(1), 0, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3399_CLKGATE_CON(0), 5, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_dbg_core_l", "armclkl", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(1), 8, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3399_CLKGATE_CON(0), 6, GFLAGS), + + GATE(ACLK_CORE_ADB400_CORE_L_2_CCI500, "aclk_core_adb400_core_l_2_cci500", "aclkm_core_l", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 12, GFLAGS), + GATE(ACLK_PERF_CORE_L, "aclk_perf_core_l", "aclkm_core_l", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 13, GFLAGS), + + GATE(0, "clk_dbg_pd_core_l", "armclkl", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 9, GFLAGS), + GATE(ACLK_GIC_ADB400_GIC_2_CORE_L, "aclk_core_adb400_gic_2_core_l", "armclkl", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 10, GFLAGS), + GATE(ACLK_GIC_ADB400_CORE_L_2_GIC, "aclk_core_adb400_core_l_2_gic", "armclkl", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 11, GFLAGS), + GATE(SCLK_PVTM_CORE_L, "clk_pvtm_core_l", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(0), 7, GFLAGS), + + /* big core */ + GATE(0, "clk_core_b_lpll_src", "lpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(1), 0, GFLAGS), + GATE(0, "clk_core_b_bpll_src", "bpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(1), 1, GFLAGS), + GATE(0, "clk_core_b_dpll_src", "dpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(1), 2, GFLAGS), + GATE(0, "clk_core_b_gpll_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(1), 3, GFLAGS), + + COMPOSITE_NOMUX(0, "aclkm_core_b", "armclkb", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(2), 8, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3399_CLKGATE_CON(1), 4, GFLAGS), + COMPOSITE_NOMUX(0, "atclk_core_b", "armclkb", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(3), 0, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3399_CLKGATE_CON(1), 5, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_dbg_core_b", "armclkb", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(3), 8, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3399_CLKGATE_CON(1), 6, GFLAGS), + + GATE(ACLK_CORE_ADB400_CORE_B_2_CCI500, "aclk_core_adb400_core_b_2_cci500", "aclkm_core_b", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 5, GFLAGS), + GATE(ACLK_PERF_CORE_B, "aclk_perf_core_b", "aclkm_core_b", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 6, GFLAGS), + + GATE(0, "clk_dbg_pd_core_b", "armclkb", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 1, GFLAGS), + GATE(ACLK_GIC_ADB400_GIC_2_CORE_B, "aclk_core_adb400_gic_2_core_b", "armclkb", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 3, GFLAGS), + GATE(ACLK_GIC_ADB400_CORE_B_2_GIC, "aclk_core_adb400_core_b_2_gic", "armclkb", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 4, GFLAGS), + + DIV(0, "pclken_dbg_core_b", "pclk_dbg_core_b", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(3), 13, 2, DFLAGS | CLK_DIVIDER_READ_ONLY), + + GATE(0, "pclk_dbg_cxcs_pd_core_b", "pclk_dbg_core_b", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(14), 2, GFLAGS), + + GATE(SCLK_PVTM_CORE_B, "clk_pvtm_core_b", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(1), 7, GFLAGS), + + /* gmac */ + GATE(0, "cpll_aclk_gmac_src", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(6), 9, GFLAGS), + GATE(0, "gpll_aclk_gmac_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(6), 8, GFLAGS), + COMPOSITE(0, "aclk_gmac_pre", mux_aclk_gmac_p, 0, + RK3399_CLKSEL_CON(20), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(6), 10, GFLAGS), + + GATE(ACLK_GMAC, "aclk_gmac", "aclk_gmac_pre", 0, + RK3399_CLKGATE_CON(32), 0, GFLAGS), + GATE(ACLK_GMAC_NOC, "aclk_gmac_noc", "aclk_gmac_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(32), 1, GFLAGS), + GATE(ACLK_PERF_GMAC, "aclk_perf_gmac", "aclk_gmac_pre", 0, + RK3399_CLKGATE_CON(32), 4, GFLAGS), + + COMPOSITE_NOMUX(0, "pclk_gmac_pre", "aclk_gmac_pre", 0, + RK3399_CLKSEL_CON(19), 8, 3, DFLAGS, + RK3399_CLKGATE_CON(6), 11, GFLAGS), + GATE(PCLK_GMAC, "pclk_gmac", "pclk_gmac_pre", 0, + RK3399_CLKGATE_CON(32), 2, GFLAGS), + GATE(PCLK_GMAC_NOC, "pclk_gmac_noc", "pclk_gmac_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(32), 3, GFLAGS), + + COMPOSITE(SCLK_MAC, "clk_gmac", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(20), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(5), 5, GFLAGS), + + MUX(SCLK_RMII_SRC, "clk_rmii_src", mux_rmii_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(19), 4, 1, MFLAGS), + GATE(SCLK_MACREF_OUT, "clk_mac_refout", "clk_rmii_src", 0, + RK3399_CLKGATE_CON(5), 6, GFLAGS), + GATE(SCLK_MACREF, "clk_mac_ref", "clk_rmii_src", 0, + RK3399_CLKGATE_CON(5), 7, GFLAGS), + GATE(SCLK_MAC_RX, "clk_rmii_rx", "clk_rmii_src", 0, + RK3399_CLKGATE_CON(5), 8, GFLAGS), + GATE(SCLK_MAC_TX, "clk_rmii_tx", "clk_rmii_src", 0, + RK3399_CLKGATE_CON(5), 9, GFLAGS), + + /* spdif */ + COMPOSITE(0, "clk_spdif_div", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(32), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(8), 13, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(99), 0, + RK3399_CLKGATE_CON(8), 14, GFLAGS, + &rk3399_spdif_fracmux), + GATE(SCLK_SPDIF_8CH, "clk_spdif", "clk_spdif_mux", CLK_SET_RATE_PARENT, + RK3399_CLKGATE_CON(8), 15, GFLAGS), + + COMPOSITE(SCLK_SPDIF_REC_DPTX, "clk_spdif_rec_dptx", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(32), 15, 1, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 6, GFLAGS), + /* i2s */ + COMPOSITE(0, "clk_i2s0_div", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(28), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(8), 3, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(96), 0, + RK3399_CLKGATE_CON(8), 4, GFLAGS, + &rk3399_i2s0_fracmux), + GATE(SCLK_I2S0_8CH, "clk_i2s0", "clk_i2s0_mux", CLK_SET_RATE_PARENT, + RK3399_CLKGATE_CON(8), 5, GFLAGS), + + COMPOSITE(0, "clk_i2s1_div", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(29), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(8), 6, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(97), 0, + RK3399_CLKGATE_CON(8), 7, GFLAGS, + &rk3399_i2s1_fracmux), + GATE(SCLK_I2S1_8CH, "clk_i2s1", "clk_i2s1_mux", CLK_SET_RATE_PARENT, + RK3399_CLKGATE_CON(8), 8, GFLAGS), + + COMPOSITE(0, "clk_i2s2_div", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(30), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(8), 9, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(98), 0, + RK3399_CLKGATE_CON(8), 10, GFLAGS, + &rk3399_i2s2_fracmux), + GATE(SCLK_I2S2_8CH, "clk_i2s2", "clk_i2s2_mux", CLK_SET_RATE_PARENT, + RK3399_CLKGATE_CON(8), 11, GFLAGS), + + MUX(0, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(31), 0, 2, MFLAGS), + COMPOSITE_NODIV(SCLK_I2S_8CH_OUT, "clk_i2sout", mux_i2sout_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(30), 8, 2, MFLAGS, + RK3399_CLKGATE_CON(8), 12, GFLAGS), + + /* uart */ + MUX(0, "clk_uart0_src", mux_pll_src_cpll_gpll_upll_p, 0, + RK3399_CLKSEL_CON(33), 12, 2, MFLAGS), + COMPOSITE_NOMUX(0, "clk_uart0_div", "clk_uart0_src", 0, + RK3399_CLKSEL_CON(33), 0, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 0, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(100), 0, + RK3399_CLKGATE_CON(9), 1, GFLAGS, + &rk3399_uart0_fracmux), + + MUX(0, "clk_uart_src", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(33), 15, 1, MFLAGS), + COMPOSITE_NOMUX(0, "clk_uart1_div", "clk_uart_src", 0, + RK3399_CLKSEL_CON(34), 0, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 2, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(101), 0, + RK3399_CLKGATE_CON(9), 3, GFLAGS, + &rk3399_uart1_fracmux), + + COMPOSITE_NOMUX(0, "clk_uart2_div", "clk_uart_src", 0, + RK3399_CLKSEL_CON(35), 0, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 4, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(102), 0, + RK3399_CLKGATE_CON(9), 5, GFLAGS, + &rk3399_uart2_fracmux), + + COMPOSITE_NOMUX(0, "clk_uart3_div", "clk_uart_src", 0, + RK3399_CLKSEL_CON(36), 0, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 6, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart3_frac", "clk_uart3_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(103), 0, + RK3399_CLKGATE_CON(9), 7, GFLAGS, + &rk3399_uart3_fracmux), + + COMPOSITE(0, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(6), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(3), 4, GFLAGS), + + GATE(PCLK_CENTER_MAIN_NOC, "pclk_center_main_noc", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(18), 10, GFLAGS), + GATE(PCLK_DDR_MON, "pclk_ddr_mon", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(18), 12, GFLAGS), + GATE(PCLK_CIC, "pclk_cic", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(18), 15, GFLAGS), + GATE(PCLK_DDR_SGRF, "pclk_ddr_sgrf", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(19), 2, GFLAGS), + + GATE(SCLK_PVTM_DDR, "clk_pvtm_ddr", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(4), 11, GFLAGS), + GATE(SCLK_DFIMON0_TIMER, "clk_dfimon0_timer", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(3), 5, GFLAGS), + GATE(SCLK_DFIMON1_TIMER, "clk_dfimon1_timer", "xin24m", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(3), 6, GFLAGS), + + /* cci */ + GATE(0, "cpll_aclk_cci_src", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 0, GFLAGS), + GATE(0, "gpll_aclk_cci_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 1, GFLAGS), + GATE(0, "npll_aclk_cci_src", "npll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 2, GFLAGS), + GATE(0, "vpll_aclk_cci_src", "vpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 3, GFLAGS), + + COMPOSITE(0, "aclk_cci_pre", mux_aclk_cci_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(5), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(2), 4, GFLAGS), + + GATE(ACLK_ADB400M_PD_CORE_L, "aclk_adb400m_pd_core_l", "aclk_cci_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 0, GFLAGS), + GATE(ACLK_ADB400M_PD_CORE_B, "aclk_adb400m_pd_core_b", "aclk_cci_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 1, GFLAGS), + GATE(ACLK_CCI, "aclk_cci", "aclk_cci_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 2, GFLAGS), + GATE(ACLK_CCI_NOC0, "aclk_cci_noc0", "aclk_cci_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 3, GFLAGS), + GATE(ACLK_CCI_NOC1, "aclk_cci_noc1", "aclk_cci_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 4, GFLAGS), + GATE(ACLK_CCI_GRF, "aclk_cci_grf", "aclk_cci_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 7, GFLAGS), + + GATE(0, "cpll_cci_trace", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 5, GFLAGS), + GATE(0, "gpll_cci_trace", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 6, GFLAGS), + COMPOSITE(SCLK_CCI_TRACE, "clk_cci_trace", mux_cci_trace_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(5), 15, 2, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(2), 7, GFLAGS), + + GATE(0, "cpll_cs", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 8, GFLAGS), + GATE(0, "gpll_cs", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 9, GFLAGS), + GATE(0, "npll_cs", "npll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(2), 10, GFLAGS), + COMPOSITE_NOGATE(0, "clk_cs", mux_cs_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(4), 6, 2, MFLAGS, 0, 5, DFLAGS), + GATE(0, "clk_dbg_cxcs", "clk_cs", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 5, GFLAGS), + GATE(0, "clk_dbg_noc", "clk_cs", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(15), 6, GFLAGS), + + /* vcodec */ + COMPOSITE(0, "aclk_vcodec_pre", mux_pll_src_cpll_gpll_npll_ppll_p, 0, + RK3399_CLKSEL_CON(7), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 0, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_vcodec_pre", "aclk_vcodec_pre", 0, + RK3399_CLKSEL_CON(7), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 1, GFLAGS), + GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0, + RK3399_CLKGATE_CON(17), 2, GFLAGS), + GATE(0, "hclk_vcodec_noc", "hclk_vcodec_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(17), 3, GFLAGS), + + GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vcodec_pre", 0, + RK3399_CLKGATE_CON(17), 0, GFLAGS), + GATE(0, "aclk_vcodec_noc", "aclk_vcodec_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(17), 1, GFLAGS), + + /* vdu */ + COMPOSITE(SCLK_VDU_CORE, "clk_vdu_core", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(9), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 4, GFLAGS), + COMPOSITE(SCLK_VDU_CA, "clk_vdu_ca", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(9), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 5, GFLAGS), + + COMPOSITE(0, "aclk_vdu_pre", mux_pll_src_cpll_gpll_npll_ppll_p, 0, + RK3399_CLKSEL_CON(8), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 2, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_vdu_pre", "aclk_vdu_pre", 0, + RK3399_CLKSEL_CON(8), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 3, GFLAGS), + GATE(HCLK_VDU, "hclk_vdu", "hclk_vdu_pre", 0, + RK3399_CLKGATE_CON(17), 10, GFLAGS), + GATE(HCLK_VDU_NOC, "hclk_vdu_noc", "hclk_vdu_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(17), 11, GFLAGS), + + GATE(ACLK_VDU, "aclk_vdu", "aclk_vdu_pre", 0, + RK3399_CLKGATE_CON(17), 8, GFLAGS), + GATE(ACLK_VDU_NOC, "aclk_vdu_noc", "aclk_vdu_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(17), 9, GFLAGS), + + /* iep */ + COMPOSITE(0, "aclk_iep_pre", mux_pll_src_cpll_gpll_npll_ppll_p, 0, + RK3399_CLKSEL_CON(10), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 6, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_iep_pre", "aclk_iep_pre", 0, + RK3399_CLKSEL_CON(10), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 7, GFLAGS), + GATE(HCLK_IEP, "hclk_iep", "hclk_iep_pre", 0, + RK3399_CLKGATE_CON(16), 2, GFLAGS), + GATE(HCLK_IEP_NOC, "hclk_iep_noc", "hclk_iep_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(16), 3, GFLAGS), + + GATE(ACLK_IEP, "aclk_iep", "aclk_iep_pre", 0, + RK3399_CLKGATE_CON(16), 0, GFLAGS), + GATE(ACLK_IEP_NOC, "aclk_iep_noc", "aclk_iep_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(16), 1, GFLAGS), + + /* rga */ + COMPOSITE(SCLK_RGA_CORE, "clk_rga_core", mux_pll_src_cpll_gpll_npll_ppll_p, 0, + RK3399_CLKSEL_CON(12), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 10, GFLAGS), + + COMPOSITE(0, "aclk_rga_pre", mux_pll_src_cpll_gpll_npll_ppll_p, 0, + RK3399_CLKSEL_CON(11), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 8, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_rga_pre", "aclk_rga_pre", 0, + RK3399_CLKSEL_CON(11), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(4), 9, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "hclk_rga_pre", 0, + RK3399_CLKGATE_CON(16), 10, GFLAGS), + GATE(HCLK_RGA_NOC, "hclk_rga_noc", "hclk_rga_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(16), 11, GFLAGS), + + GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, + RK3399_CLKGATE_CON(16), 8, GFLAGS), + GATE(ACLK_RGA_NOC, "aclk_rga_noc", "aclk_rga_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(16), 9, GFLAGS), + + /* center */ + COMPOSITE(0, "aclk_center", mux_pll_src_cpll_gpll_npll_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(3), 7, GFLAGS), + GATE(ACLK_CENTER_MAIN_NOC, "aclk_center_main_noc", "aclk_center", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(19), 0, GFLAGS), + GATE(ACLK_CENTER_PERI_NOC, "aclk_center_peri_noc", "aclk_center", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(19), 1, GFLAGS), + + /* gpu */ + COMPOSITE(0, "aclk_gpu_pre", mux_pll_src_ppll_cpll_gpll_npll_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(13), 5, 3, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 0, GFLAGS), + GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", 0, + RK3399_CLKGATE_CON(30), 8, GFLAGS), + GATE(ACLK_PERF_GPU, "aclk_perf_gpu", "aclk_gpu_pre", 0, + RK3399_CLKGATE_CON(30), 10, GFLAGS), + GATE(ACLK_GPU_GRF, "aclk_gpu_grf", "aclk_gpu_pre", 0, + RK3399_CLKGATE_CON(30), 11, GFLAGS), + GATE(SCLK_PVTM_GPU, "aclk_pvtm_gpu", "xin24m", 0, + RK3399_CLKGATE_CON(13), 1, GFLAGS), + + /* perihp */ + GATE(0, "cpll_aclk_perihp_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(5), 0, GFLAGS), + GATE(0, "gpll_aclk_perihp_src", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(5), 1, GFLAGS), + COMPOSITE(ACLK_PERIHP, "aclk_perihp", mux_aclk_perihp_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(14), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(5), 2, GFLAGS), + COMPOSITE_NOMUX(HCLK_PERIHP, "hclk_perihp", "aclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(14), 8, 2, DFLAGS, + RK3399_CLKGATE_CON(5), 3, GFLAGS), + COMPOSITE_NOMUX(PCLK_PERIHP, "pclk_perihp", "aclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(14), 12, 2, DFLAGS, + RK3399_CLKGATE_CON(5), 4, GFLAGS), + + GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 2, GFLAGS), + GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 10, GFLAGS), + GATE(0, "aclk_perihp_noc", "aclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 12, GFLAGS), + + GATE(HCLK_HOST0, "hclk_host0", "hclk_perihp", 0, + RK3399_CLKGATE_CON(20), 5, GFLAGS), + GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_perihp", 0, + RK3399_CLKGATE_CON(20), 6, GFLAGS), + GATE(HCLK_HOST1, "hclk_host1", "hclk_perihp", 0, + RK3399_CLKGATE_CON(20), 7, GFLAGS), + GATE(HCLK_HOST1_ARB, "hclk_host1_arb", "hclk_perihp", 0, + RK3399_CLKGATE_CON(20), 8, GFLAGS), + GATE(HCLK_HSIC, "hclk_hsic", "hclk_perihp", 0, + RK3399_CLKGATE_CON(20), 9, GFLAGS), + GATE(0, "hclk_perihp_noc", "hclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 13, GFLAGS), + GATE(0, "hclk_ahb1tom", "hclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 15, GFLAGS), + + GATE(PCLK_PERIHP_GRF, "pclk_perihp_grf", "pclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 4, GFLAGS), + GATE(PCLK_PCIE, "pclk_pcie", "pclk_perihp", 0, + RK3399_CLKGATE_CON(20), 11, GFLAGS), + GATE(0, "pclk_perihp_noc", "pclk_perihp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(20), 14, GFLAGS), + GATE(PCLK_HSICPHY, "pclk_hsicphy", "pclk_perihp", 0, + RK3399_CLKGATE_CON(31), 8, GFLAGS), + + /* sdio & sdmmc */ + COMPOSITE(0, "hclk_sd", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(13), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 13, GFLAGS), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_sd", 0, + RK3399_CLKGATE_CON(33), 8, GFLAGS), + GATE(0, "hclk_sdmmc_noc", "hclk_sd", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(33), 9, GFLAGS), + + COMPOSITE(SCLK_SDIO, "clk_sdio", mux_pll_src_cpll_gpll_npll_ppll_upll_24m_p, 0, + RK3399_CLKSEL_CON(15), 8, 3, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(6), 0, GFLAGS), + + COMPOSITE(SCLK_SDMMC, "clk_sdmmc", mux_pll_src_cpll_gpll_npll_ppll_upll_24m_p, 0, + RK3399_CLKSEL_CON(16), 8, 3, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(6), 1, GFLAGS), + + MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc", RK3399_SDMMC_CON0, 1), + MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc", RK3399_SDMMC_CON1, 1), + + MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio", RK3399_SDIO_CON0, 1), + MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio", RK3399_SDIO_CON1, 1), + + /* pcie */ + COMPOSITE(SCLK_PCIE_PM, "clk_pcie_pm", mux_pll_src_cpll_gpll_npll_24m_p, 0, + RK3399_CLKSEL_CON(17), 8, 3, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(6), 2, GFLAGS), + + COMPOSITE_NOMUX(SCLK_PCIEPHY_REF100M, "clk_pciephy_ref100m", "npll", 0, + RK3399_CLKSEL_CON(18), 11, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 6, GFLAGS), + MUX(SCLK_PCIEPHY_REF, "clk_pciephy_ref", mux_pll_src_24m_pciephy_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(18), 10, 1, MFLAGS), + + COMPOSITE(0, "clk_pcie_core_cru", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(18), 8, 2, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(6), 3, GFLAGS), + MUX(SCLK_PCIE_CORE, "clk_pcie_core", mux_pciecore_cru_phy_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(18), 7, 1, MFLAGS), + + /* emmc */ + COMPOSITE(SCLK_EMMC, "clk_emmc", mux_pll_src_cpll_gpll_npll_upll_24m_p, 0, + RK3399_CLKSEL_CON(22), 8, 3, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(6), 14, GFLAGS), + + GATE(0, "cpll_aclk_emmc_src", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(6), 12, GFLAGS), + GATE(0, "gpll_aclk_emmc_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(6), 13, GFLAGS), + COMPOSITE_NOGATE(ACLK_EMMC, "aclk_emmc", mux_aclk_emmc_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(21), 7, 1, MFLAGS, 0, 5, DFLAGS), + GATE(ACLK_EMMC_CORE, "aclk_emmccore", "aclk_emmc", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(32), 8, GFLAGS), + GATE(ACLK_EMMC_NOC, "aclk_emmc_noc", "aclk_emmc", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(32), 9, GFLAGS), + GATE(ACLK_EMMC_GRF, "aclk_emmcgrf", "aclk_emmc", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(32), 10, GFLAGS), + + /* perilp0 */ + GATE(0, "cpll_aclk_perilp0_src", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(7), 1, GFLAGS), + GATE(0, "gpll_aclk_perilp0_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(7), 0, GFLAGS), + COMPOSITE(ACLK_PERILP0, "aclk_perilp0", mux_aclk_perilp0_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(23), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(7), 2, GFLAGS), + COMPOSITE_NOMUX(HCLK_PERILP0, "hclk_perilp0", "aclk_perilp0", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(23), 8, 2, DFLAGS, + RK3399_CLKGATE_CON(7), 3, GFLAGS), + COMPOSITE_NOMUX(PCLK_PERILP0, "pclk_perilp0", "aclk_perilp0", 0, + RK3399_CLKSEL_CON(23), 12, 3, DFLAGS, + RK3399_CLKGATE_CON(7), 4, GFLAGS), + + /* aclk_perilp0 gates */ + GATE(ACLK_INTMEM, "aclk_intmem", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 0, GFLAGS), + GATE(ACLK_TZMA, "aclk_tzma", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 1, GFLAGS), + GATE(SCLK_INTMEM0, "clk_intmem0", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 2, GFLAGS), + GATE(SCLK_INTMEM1, "clk_intmem1", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 3, GFLAGS), + GATE(SCLK_INTMEM2, "clk_intmem2", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 4, GFLAGS), + GATE(SCLK_INTMEM3, "clk_intmem3", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 5, GFLAGS), + GATE(SCLK_INTMEM4, "clk_intmem4", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 6, GFLAGS), + GATE(SCLK_INTMEM5, "clk_intmem5", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 7, GFLAGS), + GATE(ACLK_DCF, "aclk_dcf", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 8, GFLAGS), + GATE(ACLK_DMAC0_PERILP, "aclk_dmac0_perilp", "aclk_perilp0", 0, RK3399_CLKGATE_CON(25), 5, GFLAGS), + GATE(ACLK_DMAC1_PERILP, "aclk_dmac1_perilp", "aclk_perilp0", 0, RK3399_CLKGATE_CON(25), 6, GFLAGS), + GATE(ACLK_PERILP0_NOC, "aclk_perilp0_noc", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 7, GFLAGS), + + /* hclk_perilp0 gates */ + GATE(HCLK_ROM, "hclk_rom", "hclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(24), 4, GFLAGS), + GATE(HCLK_M_CRYPTO0, "hclk_m_crypto0", "hclk_perilp0", 0, RK3399_CLKGATE_CON(24), 5, GFLAGS), + GATE(HCLK_S_CRYPTO0, "hclk_s_crypto0", "hclk_perilp0", 0, RK3399_CLKGATE_CON(24), 6, GFLAGS), + GATE(HCLK_M_CRYPTO1, "hclk_m_crypto1", "hclk_perilp0", 0, RK3399_CLKGATE_CON(24), 14, GFLAGS), + GATE(HCLK_S_CRYPTO1, "hclk_s_crypto1", "hclk_perilp0", 0, RK3399_CLKGATE_CON(24), 15, GFLAGS), + GATE(HCLK_PERILP0_NOC, "hclk_perilp0_noc", "hclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 8, GFLAGS), + + /* pclk_perilp0 gates */ + GATE(PCLK_DCF, "pclk_dcf", "pclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 9, GFLAGS), + + /* crypto */ + COMPOSITE(SCLK_CRYPTO0, "clk_crypto0", mux_pll_src_cpll_gpll_ppll_p, 0, + RK3399_CLKSEL_CON(24), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(7), 7, GFLAGS), + + COMPOSITE(SCLK_CRYPTO1, "clk_crypto1", mux_pll_src_cpll_gpll_ppll_p, 0, + RK3399_CLKSEL_CON(26), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(7), 8, GFLAGS), + + /* cm0s_perilp */ + GATE(0, "cpll_fclk_cm0s_src", "cpll", 0, + RK3399_CLKGATE_CON(7), 6, GFLAGS), + GATE(0, "gpll_fclk_cm0s_src", "gpll", 0, + RK3399_CLKGATE_CON(7), 5, GFLAGS), + COMPOSITE(FCLK_CM0S, "fclk_cm0s", mux_fclk_cm0s_p, 0, + RK3399_CLKSEL_CON(24), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(7), 9, GFLAGS), + + /* fclk_cm0s gates */ + GATE(SCLK_M0_PERILP, "sclk_m0_perilp", "fclk_cm0s", 0, RK3399_CLKGATE_CON(24), 8, GFLAGS), + GATE(HCLK_M0_PERILP, "hclk_m0_perilp", "fclk_cm0s", 0, RK3399_CLKGATE_CON(24), 9, GFLAGS), + GATE(DCLK_M0_PERILP, "dclk_m0_perilp", "fclk_cm0s", 0, RK3399_CLKGATE_CON(24), 10, GFLAGS), + GATE(SCLK_M0_PERILP_DEC, "clk_m0_perilp_dec", "fclk_cm0s", 0, RK3399_CLKGATE_CON(24), 11, GFLAGS), + GATE(HCLK_M0_PERILP_NOC, "hclk_m0_perilp_noc", "fclk_cm0s", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 11, GFLAGS), + + /* perilp1 */ + GATE(0, "cpll_hclk_perilp1_src", "cpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(8), 1, GFLAGS), + GATE(0, "gpll_hclk_perilp1_src", "gpll", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(8), 0, GFLAGS), + COMPOSITE_NOGATE(HCLK_PERILP1, "hclk_perilp1", mux_hclk_perilp1_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(25), 7, 1, MFLAGS, 0, 5, DFLAGS), + COMPOSITE_NOMUX(PCLK_PERILP1, "pclk_perilp1", "hclk_perilp1", CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(25), 8, 3, DFLAGS, + RK3399_CLKGATE_CON(8), 2, GFLAGS), + + /* hclk_perilp1 gates */ + GATE(0, "hclk_perilp1_noc", "hclk_perilp1", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 9, GFLAGS), + GATE(0, "hclk_sdio_noc", "hclk_perilp1", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 12, GFLAGS), + GATE(HCLK_I2S0_8CH, "hclk_i2s0", "hclk_perilp1", 0, RK3399_CLKGATE_CON(34), 0, GFLAGS), + GATE(HCLK_I2S1_8CH, "hclk_i2s1", "hclk_perilp1", 0, RK3399_CLKGATE_CON(34), 1, GFLAGS), + GATE(HCLK_I2S2_8CH, "hclk_i2s2", "hclk_perilp1", 0, RK3399_CLKGATE_CON(34), 2, GFLAGS), + GATE(HCLK_SPDIF, "hclk_spdif", "hclk_perilp1", 0, RK3399_CLKGATE_CON(34), 3, GFLAGS), + GATE(HCLK_SDIO, "hclk_sdio", "hclk_perilp1", 0, RK3399_CLKGATE_CON(34), 4, GFLAGS), + GATE(PCLK_SPI5, "pclk_spi5", "hclk_perilp1", 0, RK3399_CLKGATE_CON(34), 5, GFLAGS), + GATE(0, "hclk_sdioaudio_noc", "hclk_perilp1", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(34), 6, GFLAGS), + + /* pclk_perilp1 gates */ + GATE(PCLK_UART0, "pclk_uart0", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 0, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 1, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 2, GFLAGS), + GATE(PCLK_UART3, "pclk_uart3", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 3, GFLAGS), + GATE(PCLK_I2C7, "pclk_rki2c7", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 5, GFLAGS), + GATE(PCLK_I2C1, "pclk_rki2c1", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 6, GFLAGS), + GATE(PCLK_I2C5, "pclk_rki2c5", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 7, GFLAGS), + GATE(PCLK_I2C6, "pclk_rki2c6", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 8, GFLAGS), + GATE(PCLK_I2C2, "pclk_rki2c2", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 9, GFLAGS), + GATE(PCLK_I2C3, "pclk_rki2c3", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 10, GFLAGS), + GATE(PCLK_MAILBOX0, "pclk_mailbox0", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 11, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 12, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 13, GFLAGS), + GATE(PCLK_EFUSE1024NS, "pclk_efuse1024ns", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 14, GFLAGS), + GATE(PCLK_EFUSE1024S, "pclk_efuse1024s", "pclk_perilp1", 0, RK3399_CLKGATE_CON(22), 15, GFLAGS), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_perilp1", 0, RK3399_CLKGATE_CON(23), 10, GFLAGS), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_perilp1", 0, RK3399_CLKGATE_CON(23), 11, GFLAGS), + GATE(PCLK_SPI2, "pclk_spi2", "pclk_perilp1", 0, RK3399_CLKGATE_CON(23), 12, GFLAGS), + GATE(PCLK_SPI4, "pclk_spi4", "pclk_perilp1", 0, RK3399_CLKGATE_CON(23), 13, GFLAGS), + GATE(PCLK_PERIHP_GRF, "pclk_perilp_sgrf", "pclk_perilp1", 0, RK3399_CLKGATE_CON(24), 13, GFLAGS), + GATE(0, "pclk_perilp1_noc", "pclk_perilp1", 0, RK3399_CLKGATE_CON(25), 10, GFLAGS), + + /* saradc */ + COMPOSITE_NOMUX(SCLK_SARADC, "clk_saradc", "xin24m", 0, + RK3399_CLKSEL_CON(26), 8, 8, DFLAGS, + RK3399_CLKGATE_CON(9), 11, GFLAGS), + + /* tsadc */ + COMPOSITE(SCLK_TSADC, "clk_tsadc", mux_pll_p, 0, + RK3399_CLKSEL_CON(27), 15, 1, MFLAGS, 0, 10, DFLAGS, + RK3399_CLKGATE_CON(9), 10, GFLAGS), + + /* cif_testout */ + MUX(0, "clk_testout1_pll_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(38), 6, 2, MFLAGS), + COMPOSITE(0, "clk_testout1", mux_clk_testout1_p, 0, + RK3399_CLKSEL_CON(38), 5, 1, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 14, GFLAGS), + + MUX(0, "clk_testout2_pll_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(38), 14, 2, MFLAGS), + COMPOSITE(0, "clk_testout2", mux_clk_testout2_p, 0, + RK3399_CLKSEL_CON(38), 13, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 15, GFLAGS), + + /* vio */ + COMPOSITE(ACLK_VIO, "aclk_vio", mux_pll_src_cpll_gpll_ppll_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(42), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 10, GFLAGS), + COMPOSITE_NOMUX(PCLK_VIO, "pclk_vio", "aclk_vio", 0, + RK3399_CLKSEL_CON(43), 0, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 1, GFLAGS), + + GATE(ACLK_VIO_NOC, "aclk_vio_noc", "aclk_vio", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(29), 0, GFLAGS), + + GATE(PCLK_MIPI_DSI0, "pclk_mipi_dsi0", "pclk_vio", 0, + RK3399_CLKGATE_CON(29), 1, GFLAGS), + GATE(PCLK_MIPI_DSI1, "pclk_mipi_dsi1", "pclk_vio", 0, + RK3399_CLKGATE_CON(29), 2, GFLAGS), + GATE(PCLK_VIO_GRF, "pclk_vio_grf", "pclk_vio", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(29), 12, GFLAGS), + + /* hdcp */ + COMPOSITE(ACLK_HDCP, "aclk_hdcp", mux_pll_src_cpll_gpll_ppll_p, 0, + RK3399_CLKSEL_CON(42), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 12, GFLAGS), + COMPOSITE_NOMUX(HCLK_HDCP, "hclk_hdcp", "aclk_hdcp", 0, + RK3399_CLKSEL_CON(43), 5, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 3, GFLAGS), + COMPOSITE_NOMUX(PCLK_HDCP, "pclk_hdcp", "aclk_hdcp", 0, + RK3399_CLKSEL_CON(43), 10, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 10, GFLAGS), + + GATE(ACLK_HDCP_NOC, "aclk_hdcp_noc", "aclk_hdcp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(29), 4, GFLAGS), + GATE(ACLK_HDCP22, "aclk_hdcp22", "aclk_hdcp", 0, + RK3399_CLKGATE_CON(29), 10, GFLAGS), + + GATE(HCLK_HDCP_NOC, "hclk_hdcp_noc", "hclk_hdcp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(29), 5, GFLAGS), + GATE(HCLK_HDCP22, "hclk_hdcp22", "hclk_hdcp", 0, + RK3399_CLKGATE_CON(29), 9, GFLAGS), + + GATE(PCLK_HDCP_NOC, "pclk_hdcp_noc", "pclk_hdcp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(29), 3, GFLAGS), + GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "pclk_hdcp", 0, + RK3399_CLKGATE_CON(29), 6, GFLAGS), + GATE(PCLK_DP_CTRL, "pclk_dp_ctrl", "pclk_hdcp", 0, + RK3399_CLKGATE_CON(29), 7, GFLAGS), + GATE(PCLK_HDCP22, "pclk_hdcp22", "pclk_hdcp", 0, + RK3399_CLKGATE_CON(29), 8, GFLAGS), + GATE(PCLK_GASKET, "pclk_gasket", "pclk_hdcp", 0, + RK3399_CLKGATE_CON(29), 11, GFLAGS), + + /* edp */ + COMPOSITE(SCLK_DP_CORE, "clk_dp_core", mux_pll_src_npll_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(46), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 8, GFLAGS), + + COMPOSITE(PCLK_EDP, "pclk_edp", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(44), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 11, GFLAGS), + GATE(PCLK_EDP_NOC, "pclk_edp_noc", "pclk_edp", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(32), 12, GFLAGS), + GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "pclk_edp", 0, + RK3399_CLKGATE_CON(32), 13, GFLAGS), + + /* hdmi */ + GATE(SCLK_HDMI_SFR, "clk_hdmi_sfr", "xin24m", 0, + RK3399_CLKGATE_CON(11), 6, GFLAGS), + + COMPOSITE(SCLK_HDMI_CEC, "clk_hdmi_cec", mux_pll_p, 0, + RK3399_CLKSEL_CON(45), 15, 1, MFLAGS, 0, 10, DFLAGS, + RK3399_CLKGATE_CON(11), 7, GFLAGS), + + /* vop0 */ + COMPOSITE(ACLK_VOP0_PRE, "aclk_vop0_pre", mux_pll_src_vpll_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(47), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 8, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_vop0_pre", "aclk_vop0_pre", 0, + RK3399_CLKSEL_CON(47), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 9, GFLAGS), + + GATE(ACLK_VOP0, "aclk_vop0", "aclk_vop0_pre", 0, + RK3399_CLKGATE_CON(28), 3, GFLAGS), + GATE(ACLK_VOP0_NOC, "aclk_vop0_noc", "aclk_vop0_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(28), 1, GFLAGS), + + GATE(HCLK_VOP0, "hclk_vop0", "hclk_vop0_pre", 0, + RK3399_CLKGATE_CON(28), 2, GFLAGS), + GATE(HCLK_VOP0_NOC, "hclk_vop0_noc", "hclk_vop0_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(28), 0, GFLAGS), + + COMPOSITE(DCLK_VOP0_DIV, "dclk_vop0_div", mux_pll_src_vpll_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(49), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK3399_CLKGATE_CON(10), 12, GFLAGS), + + COMPOSITE_FRACMUX_NOGATE(0, "dclk_vop0_frac", "dclk_vop0_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(106), 0, + &rk3399_dclk_vop0_fracmux), + + COMPOSITE(SCLK_VOP0_PWM, "clk_vop0_pwm", mux_pll_src_vpll_cpll_gpll_24m_p, 0, + RK3399_CLKSEL_CON(51), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 14, GFLAGS), + + /* vop1 */ + COMPOSITE(ACLK_VOP1_PRE, "aclk_vop1_pre", mux_pll_src_vpll_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(48), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 10, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_vop1_pre", "aclk_vop1_pre", 0, + RK3399_CLKSEL_CON(48), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 11, GFLAGS), + + GATE(ACLK_VOP1, "aclk_vop1", "aclk_vop1_pre", 0, + RK3399_CLKGATE_CON(28), 7, GFLAGS), + GATE(ACLK_VOP1_NOC, "aclk_vop1_noc", "aclk_vop1_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(28), 5, GFLAGS), + + GATE(HCLK_VOP1, "hclk_vop1", "hclk_vop1_pre", 0, + RK3399_CLKGATE_CON(28), 6, GFLAGS), + GATE(HCLK_VOP1_NOC, "hclk_vop1_noc", "hclk_vop1_pre", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(28), 4, GFLAGS), + + COMPOSITE(DCLK_VOP1_DIV, "dclk_vop1_div", mux_pll_src_vpll_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(50), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK3399_CLKGATE_CON(10), 13, GFLAGS), + + COMPOSITE_FRACMUX_NOGATE(0, "dclk_vop1_frac", "dclk_vop1_div", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(107), 0, + &rk3399_dclk_vop1_fracmux), + + COMPOSITE(SCLK_VOP1_PWM, "clk_vop1_pwm", mux_pll_src_vpll_cpll_gpll_24m_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(52), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(10), 15, GFLAGS), + + /* isp */ + COMPOSITE(ACLK_ISP0, "aclk_isp0", mux_pll_src_cpll_gpll_ppll_p, 0, + RK3399_CLKSEL_CON(53), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 8, GFLAGS), + COMPOSITE_NOMUX(HCLK_ISP0, "hclk_isp0", "aclk_isp0", 0, + RK3399_CLKSEL_CON(53), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 9, GFLAGS), + + GATE(ACLK_ISP0_NOC, "aclk_isp0_noc", "aclk_isp0", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(27), 1, GFLAGS), + GATE(ACLK_ISP0_WRAPPER, "aclk_isp0_wrapper", "aclk_isp0", 0, + RK3399_CLKGATE_CON(27), 5, GFLAGS), + GATE(HCLK_ISP1_WRAPPER, "hclk_isp1_wrapper", "aclk_isp0", 0, + RK3399_CLKGATE_CON(27), 7, GFLAGS), + + GATE(HCLK_ISP0_NOC, "hclk_isp0_noc", "hclk_isp0", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(27), 0, GFLAGS), + GATE(HCLK_ISP0_WRAPPER, "hclk_isp0_wrapper", "hclk_isp0", 0, + RK3399_CLKGATE_CON(27), 4, GFLAGS), + + COMPOSITE(SCLK_ISP0, "clk_isp0", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(55), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 4, GFLAGS), + + COMPOSITE(ACLK_ISP1, "aclk_isp1", mux_pll_src_cpll_gpll_ppll_p, 0, + RK3399_CLKSEL_CON(54), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 10, GFLAGS), + COMPOSITE_NOMUX(HCLK_ISP1, "hclk_isp1", "aclk_isp1", 0, + RK3399_CLKSEL_CON(54), 8, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 11, GFLAGS), + + GATE(ACLK_ISP1_NOC, "aclk_isp1_noc", "aclk_isp1", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(27), 3, GFLAGS), + + GATE(HCLK_ISP1_NOC, "hclk_isp1_noc", "hclk_isp1", CLK_IGNORE_UNUSED, + RK3399_CLKGATE_CON(27), 2, GFLAGS), + GATE(ACLK_ISP1_WRAPPER, "aclk_isp1_wrapper", "hclk_isp1", 0, + RK3399_CLKGATE_CON(27), 8, GFLAGS), + + COMPOSITE(SCLK_ISP1, "clk_isp1", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(55), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(11), 5, GFLAGS), + + /* + * We use pclkin_cifinv by default GRF_SOC_CON20[9] (GSC20_9) setting in system, + * so we ignore the mux and make clocks nodes as following, + * + * pclkin_cifinv --|-------\ + * |GSC20_9|-- pclkin_cifmux -- |G27_6| -- pclkin_isp1_wrapper + * pclkin_cif --|-------/ + */ + GATE(PCLK_ISP1_WRAPPER, "pclkin_isp1_wrapper", "pclkin_cif", 0, + RK3399_CLKGATE_CON(27), 6, GFLAGS), + + /* cif */ + COMPOSITE_NODIV(0, "clk_cifout_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(56), 6, 2, MFLAGS, + RK3399_CLKGATE_CON(10), 7, GFLAGS), + + COMPOSITE_NOGATE(SCLK_CIF_OUT, "clk_cifout", mux_clk_cif_p, 0, + RK3399_CLKSEL_CON(56), 5, 1, MFLAGS, 0, 5, DFLAGS), + + /* gic */ + COMPOSITE(ACLK_GIC_PRE, "aclk_gic_pre", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED, + RK3399_CLKSEL_CON(56), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(12), 12, GFLAGS), + + GATE(ACLK_GIC, "aclk_gic", "aclk_gic_pre", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(33), 0, GFLAGS), + GATE(ACLK_GIC_NOC, "aclk_gic_noc", "aclk_gic_pre", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(33), 1, GFLAGS), + GATE(ACLK_GIC_ADB400_CORE_L_2_GIC, "aclk_gic_adb400_core_l_2_gic", "aclk_gic_pre", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(33), 2, GFLAGS), + GATE(ACLK_GIC_ADB400_CORE_B_2_GIC, "aclk_gic_adb400_core_b_2_gic", "aclk_gic_pre", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(33), 3, GFLAGS), + GATE(ACLK_GIC_ADB400_GIC_2_CORE_L, "aclk_gic_adb400_gic_2_core_l", "aclk_gic_pre", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(33), 4, GFLAGS), + GATE(ACLK_GIC_ADB400_GIC_2_CORE_B, "aclk_gic_adb400_gic_2_core_b", "aclk_gic_pre", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(33), 5, GFLAGS), + + /* alive */ + /* pclk_alive_gpll_src is controlled by PMUGRF_SOC_CON0[6] */ + DIV(PCLK_ALIVE, "pclk_alive", "gpll", 0, + RK3399_CLKSEL_CON(57), 0, 5, DFLAGS), + + GATE(PCLK_USBPHY_MUX_G, "pclk_usbphy_mux_g", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 4, GFLAGS), + GATE(PCLK_UPHY0_TCPHY_G, "pclk_uphy0_tcphy_g", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 5, GFLAGS), + GATE(PCLK_UPHY0_TCPD_G, "pclk_uphy0_tcpd_g", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 6, GFLAGS), + GATE(PCLK_UPHY1_TCPHY_G, "pclk_uphy1_tcphy_g", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 8, GFLAGS), + GATE(PCLK_UPHY1_TCPD_G, "pclk_uphy1_tcpd_g", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 9, GFLAGS), + + GATE(PCLK_GRF, "pclk_grf", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 1, GFLAGS), + GATE(PCLK_INTR_ARB, "pclk_intr_arb", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 2, GFLAGS), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_alive", 0, RK3399_CLKGATE_CON(31), 3, GFLAGS), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_alive", 0, RK3399_CLKGATE_CON(31), 4, GFLAGS), + GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_alive", 0, RK3399_CLKGATE_CON(31), 5, GFLAGS), + GATE(PCLK_TIMER0, "pclk_timer0", "pclk_alive", 0, RK3399_CLKGATE_CON(31), 6, GFLAGS), + GATE(PCLK_TIMER1, "pclk_timer1", "pclk_alive", 0, RK3399_CLKGATE_CON(31), 7, GFLAGS), + GATE(PCLK_PMU_INTR_ARB, "pclk_pmu_intr_arb", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 9, GFLAGS), + GATE(PCLK_SGRF, "pclk_sgrf", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 10, GFLAGS), + + GATE(SCLK_MIPIDPHY_REF, "clk_mipidphy_ref", "xin24m", 0, RK3399_CLKGATE_CON(11), 14, GFLAGS), + GATE(SCLK_DPHY_PLL, "clk_dphy_pll", "clk_mipidphy_ref", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 0, GFLAGS), + + GATE(SCLK_MIPIDPHY_CFG, "clk_mipidphy_cfg", "xin24m", 0, RK3399_CLKGATE_CON(11), 15, GFLAGS), + GATE(SCLK_DPHY_TX0_CFG, "clk_dphy_tx0_cfg", "clk_mipidphy_cfg", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 1, GFLAGS), + GATE(SCLK_DPHY_TX1RX1_CFG, "clk_dphy_tx1rx1_cfg", "clk_mipidphy_cfg", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 2, GFLAGS), + GATE(SCLK_DPHY_RX0_CFG, "clk_dphy_rx0_cfg", "clk_mipidphy_cfg", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 3, GFLAGS), + + /* testout */ + MUX(0, "clk_test_pre", mux_pll_src_cpll_gpll_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(58), 7, 1, MFLAGS), + COMPOSITE_FRAC(0, "clk_test_frac", "clk_test_pre", CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(105), 0, + RK3399_CLKGATE_CON(13), 9, GFLAGS), + + DIV(0, "clk_test_24m", "xin24m", 0, + RK3399_CLKSEL_CON(57), 6, 10, DFLAGS), + + /* spi */ + COMPOSITE(SCLK_SPI0, "clk_spi0", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(59), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 12, GFLAGS), + + COMPOSITE(SCLK_SPI1, "clk_spi1", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(59), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 13, GFLAGS), + + COMPOSITE(SCLK_SPI2, "clk_spi2", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(60), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 14, GFLAGS), + + COMPOSITE(SCLK_SPI4, "clk_spi4", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(60), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3399_CLKGATE_CON(9), 15, GFLAGS), + + COMPOSITE(SCLK_SPI5, "clk_spi5", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(58), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3399_CLKGATE_CON(13), 13, GFLAGS), + + /* i2c */ + COMPOSITE(SCLK_I2C1, "clk_i2c1", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(61), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(10), 0, GFLAGS), + + COMPOSITE(SCLK_I2C2, "clk_i2c2", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(62), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(10), 2, GFLAGS), + + COMPOSITE(SCLK_I2C3, "clk_i2c3", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(63), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_CLKGATE_CON(10), 4, GFLAGS), + + COMPOSITE(SCLK_I2C5, "clk_i2c5", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(61), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3399_CLKGATE_CON(10), 1, GFLAGS), + + COMPOSITE(SCLK_I2C6, "clk_i2c6", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(62), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3399_CLKGATE_CON(10), 3, GFLAGS), + + COMPOSITE(SCLK_I2C7, "clk_i2c7", mux_pll_src_cpll_gpll_p, 0, + RK3399_CLKSEL_CON(63), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3399_CLKGATE_CON(10), 5, GFLAGS), + + /* timer */ + GATE(SCLK_TIMER00, "clk_timer00", "xin24m", 0, RK3399_CLKGATE_CON(26), 0, GFLAGS), + GATE(SCLK_TIMER01, "clk_timer01", "xin24m", 0, RK3399_CLKGATE_CON(26), 1, GFLAGS), + GATE(SCLK_TIMER02, "clk_timer02", "xin24m", 0, RK3399_CLKGATE_CON(26), 2, GFLAGS), + GATE(SCLK_TIMER03, "clk_timer03", "xin24m", 0, RK3399_CLKGATE_CON(26), 3, GFLAGS), + GATE(SCLK_TIMER04, "clk_timer04", "xin24m", 0, RK3399_CLKGATE_CON(26), 4, GFLAGS), + GATE(SCLK_TIMER05, "clk_timer05", "xin24m", 0, RK3399_CLKGATE_CON(26), 5, GFLAGS), + GATE(SCLK_TIMER06, "clk_timer06", "xin24m", 0, RK3399_CLKGATE_CON(26), 6, GFLAGS), + GATE(SCLK_TIMER07, "clk_timer07", "xin24m", 0, RK3399_CLKGATE_CON(26), 7, GFLAGS), + GATE(SCLK_TIMER08, "clk_timer08", "xin24m", 0, RK3399_CLKGATE_CON(26), 8, GFLAGS), + GATE(SCLK_TIMER09, "clk_timer09", "xin24m", 0, RK3399_CLKGATE_CON(26), 9, GFLAGS), + GATE(SCLK_TIMER10, "clk_timer10", "xin24m", 0, RK3399_CLKGATE_CON(26), 10, GFLAGS), + GATE(SCLK_TIMER11, "clk_timer11", "xin24m", 0, RK3399_CLKGATE_CON(26), 11, GFLAGS), + + /* clk_test */ + /* clk_test_pre is controlled by CRU_MISC_CON[3] */ + COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, + RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, + RK3368_CLKGATE_CON(13), 11, GFLAGS), +}; + +static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { + /* + * PMU CRU Clock-Architecture + */ + + GATE(0, "fclk_cm0s_pmu_ppll_src", "ppll", 0, + RK3399_PMU_CLKGATE_CON(0), 1, GFLAGS), + + COMPOSITE_NOGATE(FCLK_CM0S_SRC_PMU, "fclk_cm0s_src_pmu", mux_fclk_cm0s_pmu_ppll_p, 0, + RK3399_PMU_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS), + + COMPOSITE(SCLK_SPI3_PMU, "clk_spi3_pmu", mux_24m_ppll_p, 0, + RK3399_PMU_CLKSEL_CON(1), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_PMU_CLKGATE_CON(0), 2, GFLAGS), + + COMPOSITE(0, "clk_wifi_div", mux_ppll_24m_p, CLK_IGNORE_UNUSED, + RK3399_PMU_CLKSEL_CON(1), 13, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_PMU_CLKGATE_CON(0), 8, GFLAGS), + + COMPOSITE_FRACMUX_NOGATE(0, "clk_wifi_frac", "clk_wifi_div", CLK_SET_RATE_PARENT, + RK3399_PMU_CLKSEL_CON(7), 0, + &rk3399_pmuclk_wifi_fracmux), + + MUX(0, "clk_timer_src_pmu", mux_pll_p, CLK_IGNORE_UNUSED, + RK3399_PMU_CLKSEL_CON(1), 15, 1, MFLAGS), + + COMPOSITE_NOMUX(SCLK_I2C0_PMU, "clk_i2c0_pmu", "ppll", 0, + RK3399_PMU_CLKSEL_CON(2), 0, 7, DFLAGS, + RK3399_PMU_CLKGATE_CON(0), 9, GFLAGS), + + COMPOSITE_NOMUX(SCLK_I2C4_PMU, "clk_i2c4_pmu", "ppll", 0, + RK3399_PMU_CLKSEL_CON(3), 0, 7, DFLAGS, + RK3399_PMU_CLKGATE_CON(0), 10, GFLAGS), + + COMPOSITE_NOMUX(SCLK_I2C8_PMU, "clk_i2c8_pmu", "ppll", 0, + RK3399_PMU_CLKSEL_CON(2), 8, 7, DFLAGS, + RK3399_PMU_CLKGATE_CON(0), 11, GFLAGS), + + DIV(0, "clk_32k_suspend_pmu", "xin24m", CLK_IGNORE_UNUSED, + RK3399_PMU_CLKSEL_CON(4), 0, 10, DFLAGS), + MUX(0, "clk_testout_2io", mux_clk_testout2_2io_p, CLK_IGNORE_UNUSED, + RK3399_PMU_CLKSEL_CON(4), 15, 1, MFLAGS), + + COMPOSITE(0, "clk_uart4_div", mux_24m_ppll_p, 0, + RK3399_PMU_CLKSEL_CON(5), 10, 1, MFLAGS, 0, 7, DFLAGS, + RK3399_PMU_CLKGATE_CON(0), 5, GFLAGS), + + COMPOSITE_FRACMUX(0, "clk_uart4_frac", "clk_uart4_div", CLK_SET_RATE_PARENT, + RK3399_PMU_CLKSEL_CON(6), 0, + RK3399_PMU_CLKGATE_CON(0), 6, GFLAGS, + &rk3399_uart4_pmu_fracmux), + + DIV(PCLK_SRC_PMU, "pclk_pmu_src", "ppll", CLK_IGNORE_UNUSED, + RK3399_PMU_CLKSEL_CON(0), 0, 5, DFLAGS), + + /* pmu clock gates */ + GATE(SCLK_TIMER12_PMU, "clk_timer0_pmu", "clk_timer_src_pmu", 0, RK3399_PMU_CLKGATE_CON(0), 3, GFLAGS), + GATE(SCLK_TIMER13_PMU, "clk_timer1_pmu", "clk_timer_src_pmu", 0, RK3399_PMU_CLKGATE_CON(0), 4, GFLAGS), + + GATE(SCLK_PVTM_PMU, "clk_pvtm_pmu", "xin24m", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(0), 7, GFLAGS), + + GATE(PCLK_PMU, "pclk_pmu", "pclk_pmu_src", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(1), 0, GFLAGS), + GATE(PCLK_PMUGRF_PMU, "pclk_pmugrf_pmu", "pclk_pmu_src", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(1), 1, GFLAGS), + GATE(PCLK_INTMEM1_PMU, "pclk_intmem1_pmu", "pclk_pmu_src", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(1), 2, GFLAGS), + GATE(PCLK_GPIO0_PMU, "pclk_gpio0_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 3, GFLAGS), + GATE(PCLK_GPIO1_PMU, "pclk_gpio1_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 4, GFLAGS), + GATE(PCLK_SGRF_PMU, "pclk_sgrf_pmu", "pclk_pmu_src", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(1), 5, GFLAGS), + GATE(PCLK_NOC_PMU, "pclk_noc_pmu", "pclk_pmu_src", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(1), 6, GFLAGS), + GATE(PCLK_I2C0_PMU, "pclk_i2c0_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 7, GFLAGS), + GATE(PCLK_I2C4_PMU, "pclk_i2c4_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 8, GFLAGS), + GATE(PCLK_I2C8_PMU, "pclk_i2c8_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 9, GFLAGS), + GATE(PCLK_RKPWM_PMU, "pclk_rkpwm_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 10, GFLAGS), + GATE(PCLK_SPI3_PMU, "pclk_spi3_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 11, GFLAGS), + GATE(PCLK_TIMER_PMU, "pclk_timer_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 12, GFLAGS), + GATE(PCLK_MAILBOX_PMU, "pclk_mailbox_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 13, GFLAGS), + GATE(PCLK_UART4_PMU, "pclk_uart4_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 14, GFLAGS), + GATE(PCLK_WDT_M0_PMU, "pclk_wdt_m0_pmu", "pclk_pmu_src", 0, RK3399_PMU_CLKGATE_CON(1), 15, GFLAGS), + + GATE(FCLK_CM0S_PMU, "fclk_cm0s_pmu", "fclk_cm0s_src_pmu", 0, RK3399_PMU_CLKGATE_CON(2), 0, GFLAGS), + GATE(SCLK_CM0S_PMU, "sclk_cm0s_pmu", "fclk_cm0s_src_pmu", 0, RK3399_PMU_CLKGATE_CON(2), 1, GFLAGS), + GATE(HCLK_CM0S_PMU, "hclk_cm0s_pmu", "fclk_cm0s_src_pmu", 0, RK3399_PMU_CLKGATE_CON(2), 2, GFLAGS), + GATE(DCLK_CM0S_PMU, "dclk_cm0s_pmu", "fclk_cm0s_src_pmu", 0, RK3399_PMU_CLKGATE_CON(2), 3, GFLAGS), + GATE(HCLK_NOC_PMU, "hclk_noc_pmu", "fclk_cm0s_src_pmu", CLK_IGNORE_UNUSED, RK3399_PMU_CLKGATE_CON(2), 5, GFLAGS), +}; + +static const char *const rk3399_cru_critical_clocks[] __initconst = { + "aclk_cci_pre", + "pclk_perilp0", + "pclk_perilp0", + "hclk_perilp0", + "hclk_perilp0_noc", + "pclk_perilp1", + "pclk_perilp1_noc", + "pclk_perihp", + "pclk_perihp_noc", + "hclk_perihp", + "aclk_perihp", + "aclk_perihp_noc", + "aclk_perilp0", + "aclk_perilp0_noc", + "hclk_perilp1", + "hclk_perilp1_noc", + "aclk_dmac0_perilp", + "gpll_hclk_perilp1_src", + "gpll_aclk_perilp0_src", + "gpll_aclk_perihp_src", +}; + +static const char *const rk3399_pmucru_critical_clocks[] __initconst = { + "ppll", + "pclk_pmu_src", + "fclk_cm0s_src_pmu", + "clk_timer_src_pmu", +}; + +static void __init rk3399_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + return; + } + + rockchip_clk_register_plls(ctx, rk3399_pll_clks, + ARRAY_SIZE(rk3399_pll_clks), -1); + + rockchip_clk_register_branches(ctx, rk3399_clk_branches, + ARRAY_SIZE(rk3399_clk_branches)); + + rockchip_clk_protect_critical(rk3399_cru_critical_clocks, + ARRAY_SIZE(rk3399_cru_critical_clocks)); + + rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl", + mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p), + &rk3399_cpuclkl_data, rk3399_cpuclkl_rates, + ARRAY_SIZE(rk3399_cpuclkl_rates)); + + rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb", + mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p), + &rk3399_cpuclkb_data, rk3399_cpuclkb_rates, + ARRAY_SIZE(rk3399_cpuclkb_rates)); + + rockchip_register_softrst(np, 21, reg_base + RK3399_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(ctx, RK3399_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(rk3399_cru, "rockchip,rk3399-cru", rk3399_clk_init); + +static void __init rk3399_pmu_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru pmu region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, CLKPMU_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip pmu clk init failed\n", __func__); + return; + } + + rockchip_clk_register_plls(ctx, rk3399_pmu_pll_clks, + ARRAY_SIZE(rk3399_pmu_pll_clks), -1); + + rockchip_clk_register_branches(ctx, rk3399_clk_pmu_branches, + ARRAY_SIZE(rk3399_clk_pmu_branches)); + + rockchip_clk_protect_critical(rk3399_pmucru_critical_clocks, + ARRAY_SIZE(rk3399_pmucru_critical_clocks)); + + rockchip_register_softrst(np, 2, reg_base + RK3399_PMU_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(rk3399_cru_pmu, "rockchip,rk3399-pmucru", rk3399_pmu_clk_init); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index ec06350c78c4..7ffd134995f2 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -2,6 +2,9 @@ * Copyright (c) 2014 MundoReader S.L. * Author: Heiko Stuebner <heiko@sntech.de> * + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Xing Zheng <zhengxing@rock-chips.com> + * * based on * * samsung/clk.c @@ -39,7 +42,8 @@ * sometimes without one of those components. */ static struct clk *rockchip_clk_register_branch(const char *name, - const char *const *parent_names, u8 num_parents, void __iomem *base, + const char *const *parent_names, u8 num_parents, + void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, u8 div_shift, u8 div_width, u8 div_flags, struct clk_div_table *div_table, int gate_offset, @@ -136,9 +140,11 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", __func__, event, ndata->old_rate, ndata->new_rate); if (event == PRE_RATE_CHANGE) { - frac->rate_change_idx = frac->mux_ops->get_parent(&frac_mux->hw); + frac->rate_change_idx = + frac->mux_ops->get_parent(&frac_mux->hw); if (frac->rate_change_idx != frac->mux_frac_idx) { - frac->mux_ops->set_parent(&frac_mux->hw, frac->mux_frac_idx); + frac->mux_ops->set_parent(&frac_mux->hw, + frac->mux_frac_idx); frac->rate_change_remuxed = 1; } } else if (event == POST_RATE_CHANGE) { @@ -149,7 +155,8 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, * reaches the mux itself. */ if (frac->rate_change_remuxed) { - frac->mux_ops->set_parent(&frac_mux->hw, frac->rate_change_idx); + frac->mux_ops->set_parent(&frac_mux->hw, + frac->rate_change_idx); frac->rate_change_remuxed = 0; } } @@ -157,7 +164,8 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, return notifier_from_errno(ret); } -static struct clk *rockchip_clk_register_frac_branch(const char *name, +static struct clk *rockchip_clk_register_frac_branch( + struct rockchip_clk_provider *ctx, const char *name, const char *const *parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 div_flags, int gate_offset, u8 gate_shift, u8 gate_flags, @@ -250,7 +258,7 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name, if (IS_ERR(mux_clk)) return clk; - rockchip_clk_add_lookup(mux_clk, child->id); + rockchip_clk_add_lookup(ctx, mux_clk, child->id); /* notifier on the fraction divider to catch rate changes */ if (frac->mux_frac_idx >= 0) { @@ -314,66 +322,82 @@ static struct clk *rockchip_clk_register_factor_branch(const char *name, return clk; } -static DEFINE_SPINLOCK(clk_lock); -static struct clk **clk_table; -static void __iomem *reg_base; -static struct clk_onecell_data clk_data; -static struct device_node *cru_node; -static struct regmap *grf; - -void __init rockchip_clk_init(struct device_node *np, void __iomem *base, - unsigned long nr_clks) +struct rockchip_clk_provider * __init rockchip_clk_init(struct device_node *np, + void __iomem *base, unsigned long nr_clks) { - reg_base = base; - cru_node = np; - grf = ERR_PTR(-EPROBE_DEFER); + struct rockchip_clk_provider *ctx; + struct clk **clk_table; + int i; + + ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); if (!clk_table) - pr_err("%s: could not allocate clock lookup table\n", __func__); + goto err_free; - clk_data.clks = clk_table; - clk_data.clk_num = nr_clks; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + for (i = 0; i < nr_clks; ++i) + clk_table[i] = ERR_PTR(-ENOENT); + + ctx->reg_base = base; + ctx->clk_data.clks = clk_table; + ctx->clk_data.clk_num = nr_clks; + ctx->cru_node = np; + ctx->grf = ERR_PTR(-EPROBE_DEFER); + spin_lock_init(&ctx->lock); + + ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, + "rockchip,grf"); + + return ctx; + +err_free: + kfree(ctx); + return ERR_PTR(-ENOMEM); } -struct regmap *rockchip_clk_get_grf(void) +void __init rockchip_clk_of_add_provider(struct device_node *np, + struct rockchip_clk_provider *ctx) { - if (IS_ERR(grf)) - grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf"); - return grf; + if (of_clk_add_provider(np, of_clk_src_onecell_get, + &ctx->clk_data)) + pr_err("%s: could not register clk provider\n", __func__); } -void rockchip_clk_add_lookup(struct clk *clk, unsigned int id) +void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, + struct clk *clk, unsigned int id) { - if (clk_table && id) - clk_table[id] = clk; + if (ctx->clk_data.clks && id) + ctx->clk_data.clks[id] = clk; } -void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list, +void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, + struct rockchip_pll_clock *list, unsigned int nr_pll, int grf_lock_offset) { struct clk *clk; int idx; for (idx = 0; idx < nr_pll; idx++, list++) { - clk = rockchip_clk_register_pll(list->type, list->name, + clk = rockchip_clk_register_pll(ctx, list->type, list->name, list->parent_names, list->num_parents, - reg_base, list->con_offset, grf_lock_offset, + list->con_offset, grf_lock_offset, list->lock_shift, list->mode_offset, list->mode_shift, list->rate_table, - list->pll_flags, &clk_lock); + list->pll_flags); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, list->name); continue; } - rockchip_clk_add_lookup(clk, list->id); + rockchip_clk_add_lookup(ctx, clk, list->id); } } void __init rockchip_clk_register_branches( + struct rockchip_clk_provider *ctx, struct rockchip_clk_branch *list, unsigned int nr_clk) { @@ -389,56 +413,59 @@ void __init rockchip_clk_register_branches( case branch_mux: clk = clk_register_mux(NULL, list->name, list->parent_names, list->num_parents, - flags, reg_base + list->muxdiv_offset, + flags, ctx->reg_base + list->muxdiv_offset, list->mux_shift, list->mux_width, - list->mux_flags, &clk_lock); + list->mux_flags, &ctx->lock); break; case branch_divider: if (list->div_table) clk = clk_register_divider_table(NULL, list->name, list->parent_names[0], - flags, reg_base + list->muxdiv_offset, + flags, + ctx->reg_base + list->muxdiv_offset, list->div_shift, list->div_width, list->div_flags, list->div_table, - &clk_lock); + &ctx->lock); else clk = clk_register_divider(NULL, list->name, list->parent_names[0], flags, - reg_base + list->muxdiv_offset, + ctx->reg_base + list->muxdiv_offset, list->div_shift, list->div_width, - list->div_flags, &clk_lock); + list->div_flags, &ctx->lock); break; case branch_fraction_divider: - clk = rockchip_clk_register_frac_branch(list->name, + clk = rockchip_clk_register_frac_branch(ctx, list->name, list->parent_names, list->num_parents, - reg_base, list->muxdiv_offset, list->div_flags, + ctx->reg_base, list->muxdiv_offset, + list->div_flags, list->gate_offset, list->gate_shift, list->gate_flags, flags, list->child, - &clk_lock); + &ctx->lock); break; case branch_gate: flags |= CLK_SET_RATE_PARENT; clk = clk_register_gate(NULL, list->name, list->parent_names[0], flags, - reg_base + list->gate_offset, - list->gate_shift, list->gate_flags, &clk_lock); + ctx->reg_base + list->gate_offset, + list->gate_shift, list->gate_flags, &ctx->lock); break; case branch_composite: clk = rockchip_clk_register_branch(list->name, list->parent_names, list->num_parents, - reg_base, list->muxdiv_offset, list->mux_shift, + ctx->reg_base, list->muxdiv_offset, + list->mux_shift, list->mux_width, list->mux_flags, list->div_shift, list->div_width, list->div_flags, list->div_table, list->gate_offset, list->gate_shift, - list->gate_flags, flags, &clk_lock); + list->gate_flags, flags, &ctx->lock); break; case branch_mmc: clk = rockchip_clk_register_mmc( list->name, list->parent_names, list->num_parents, - reg_base + list->muxdiv_offset, + ctx->reg_base + list->muxdiv_offset, list->div_shift ); break; @@ -446,16 +473,16 @@ void __init rockchip_clk_register_branches( clk = rockchip_clk_register_inverter( list->name, list->parent_names, list->num_parents, - reg_base + list->muxdiv_offset, - list->div_shift, list->div_flags, &clk_lock); + ctx->reg_base + list->muxdiv_offset, + list->div_shift, list->div_flags, &ctx->lock); break; case branch_factor: clk = rockchip_clk_register_factor_branch( list->name, list->parent_names, - list->num_parents, reg_base, + list->num_parents, ctx->reg_base, list->div_shift, list->div_width, list->gate_offset, list->gate_shift, - list->gate_flags, flags, &clk_lock); + list->gate_flags, flags, &ctx->lock); break; } @@ -472,11 +499,12 @@ void __init rockchip_clk_register_branches( continue; } - rockchip_clk_add_lookup(clk, list->id); + rockchip_clk_add_lookup(ctx, clk, list->id); } } -void __init rockchip_clk_register_armclk(unsigned int lookup_id, +void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, @@ -486,15 +514,15 @@ void __init rockchip_clk_register_armclk(unsigned int lookup_id, struct clk *clk; clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, - reg_data, rates, nrates, reg_base, - &clk_lock); + reg_data, rates, nrates, + ctx->reg_base, &ctx->lock); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s: %ld\n", __func__, name, PTR_ERR(clk)); return; } - rockchip_clk_add_lookup(clk, lookup_id); + rockchip_clk_add_lookup(ctx, clk, lookup_id); } void __init rockchip_clk_protect_critical(const char *const clocks[], @@ -511,6 +539,7 @@ void __init rockchip_clk_protect_critical(const char *const clocks[], } } +static void __iomem *rst_base; static unsigned int reg_restart; static void (*cb_restart)(void); static int rockchip_restart_notify(struct notifier_block *this, @@ -519,7 +548,7 @@ static int rockchip_restart_notify(struct notifier_block *this, if (cb_restart) cb_restart(); - writel(0xfdb9, reg_base + reg_restart); + writel(0xfdb9, rst_base + reg_restart); return NOTIFY_DONE; } @@ -528,10 +557,14 @@ static struct notifier_block rockchip_restart_handler = { .priority = 128, }; -void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void)) +void __init +rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, + unsigned int reg, + void (*cb)(void)) { int ret; + rst_base = ctx->reg_base; reg_restart = reg; cb_restart = cb; ret = register_restart_handler(&rockchip_restart_handler); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 39c198bbcbee..2194ffa8c9fd 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -27,13 +27,13 @@ #define CLK_ROCKCHIP_CLK_H #include <linux/io.h> +#include <linux/clk-provider.h> struct clk; #define HIWORD_UPDATE(val, mask, shift) \ ((val) << (shift) | (mask) << ((shift) + 16)) -/* register positions shared by RK2928, RK3036, RK3066, RK3188 and RK3228 */ #define RK2928_PLL_CON(x) ((x) * 0x4) #define RK2928_MODE_CON 0x40 #define RK2928_CLKSEL_CON(x) ((x) * 0x4 + 0x44) @@ -92,9 +92,30 @@ struct clk; #define RK3368_EMMC_CON0 0x418 #define RK3368_EMMC_CON1 0x41c +#define RK3399_PLL_CON(x) RK2928_PLL_CON(x) +#define RK3399_CLKSEL_CON(x) ((x) * 0x4 + 0x100) +#define RK3399_CLKGATE_CON(x) ((x) * 0x4 + 0x300) +#define RK3399_SOFTRST_CON(x) ((x) * 0x4 + 0x400) +#define RK3399_GLB_SRST_FST 0x500 +#define RK3399_GLB_SRST_SND 0x504 +#define RK3399_GLB_CNT_TH 0x508 +#define RK3399_MISC_CON 0x50c +#define RK3399_RST_CON 0x510 +#define RK3399_RST_ST 0x514 +#define RK3399_SDMMC_CON0 0x580 +#define RK3399_SDMMC_CON1 0x584 +#define RK3399_SDIO_CON0 0x588 +#define RK3399_SDIO_CON1 0x58c + +#define RK3399_PMU_PLL_CON(x) RK2928_PLL_CON(x) +#define RK3399_PMU_CLKSEL_CON(x) ((x) * 0x4 + 0x80) +#define RK3399_PMU_CLKGATE_CON(x) ((x) * 0x4 + 0x100) +#define RK3399_PMU_SOFTRST_CON(x) ((x) * 0x4 + 0x110) + enum rockchip_pll_type { pll_rk3036, pll_rk3066, + pll_rk3399, }; #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ @@ -127,13 +148,29 @@ enum rockchip_pll_type { .nb = _nb, \ } +/** + * struct rockchip_clk_provider - information about clock provider + * @reg_base: virtual address for the register base. + * @clk_data: holds clock related data like clk* and number of clocks. + * @cru_node: device-node of the clock-provider + * @grf: regmap of the general-register-files syscon + * @lock: maintains exclusion between callbacks for a given clock-provider. + */ +struct rockchip_clk_provider { + void __iomem *reg_base; + struct clk_onecell_data clk_data; + struct device_node *cru_node; + struct regmap *grf; + spinlock_t lock; +}; + struct rockchip_pll_rate_table { unsigned long rate; unsigned int nr; unsigned int nf; unsigned int no; unsigned int nb; - /* for RK3036 */ + /* for RK3036/RK3399 */ unsigned int fbdiv; unsigned int postdiv1; unsigned int refdiv; @@ -143,10 +180,11 @@ struct rockchip_pll_rate_table { }; /** - * struct rockchip_pll_clock: information about pll clock + * struct rockchip_pll_clock - information about pll clock * @id: platform specific id of the clock. * @name: name of this pll clock. - * @parent_name: name of the parent clock. + * @parent_names: name of the parent clock. + * @num_parents: number of parents * @flags: optional flags for basic clock. * @con_offset: offset of the register for configuring the PLL. * @mode_offset: offset of the register for configuring the PLL-mode. @@ -194,12 +232,13 @@ struct rockchip_pll_clock { .rate_table = _rtable, \ } -struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, +struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, + enum rockchip_pll_type pll_type, const char *name, const char *const *parent_names, - u8 num_parents, void __iomem *base, int con_offset, - int grf_lock_offset, int lock_shift, int reg_mode, - int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags, spinlock_t *lock); + u8 num_parents, int con_offset, int grf_lock_offset, + int lock_shift, int mode_offset, int mode_shift, + struct rockchip_pll_rate_table *rate_table, + u8 clk_pll_flags); struct rockchip_cpuclk_clksel { int reg; @@ -213,18 +252,23 @@ struct rockchip_cpuclk_rate_table { }; /** - * struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock + * struct rockchip_cpuclk_reg_data - register offsets and masks of the cpuclock * @core_reg: register offset of the core settings register * @div_core_shift: core divider offset used to divide the pll value * @div_core_mask: core divider mask + * @mux_core_alt: mux value to select alternate parent + * @mux_core_main: mux value to select main parent of core * @mux_core_shift: offset of the core multiplexer + * @mux_core_mask: core multiplexer mask */ struct rockchip_cpuclk_reg_data { int core_reg; u8 div_core_shift; u32 div_core_mask; - int mux_core_reg; + u8 mux_core_alt; + u8 mux_core_main; u8 mux_core_shift; + u32 mux_core_mask; }; struct clk *rockchip_clk_register_cpuclk(const char *name, @@ -428,6 +472,22 @@ struct rockchip_clk_branch { .child = ch, \ } +#define COMPOSITE_FRACMUX_NOGATE(_id, cname, pname, f, mo, df, ch) \ + { \ + .id = _id, \ + .branch_type = branch_fraction_divider, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .num_parents = 1, \ + .flags = f, \ + .muxdiv_offset = mo, \ + .div_shift = 16, \ + .div_width = 16, \ + .div_flags = df, \ + .gate_offset = -1, \ + .child = ch, \ + } + #define MUX(_id, cname, pnames, f, o, s, w, mf) \ { \ .id = _id, \ @@ -536,21 +596,27 @@ struct rockchip_clk_branch { .gate_flags = gf, \ } -void rockchip_clk_init(struct device_node *np, void __iomem *base, - unsigned long nr_clks); -struct regmap *rockchip_clk_get_grf(void); -void rockchip_clk_add_lookup(struct clk *clk, unsigned int id); -void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, +struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, + void __iomem *base, unsigned long nr_clks); +void rockchip_clk_of_add_provider(struct device_node *np, + struct rockchip_clk_provider *ctx); +void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, + struct clk *clk, unsigned int id); +void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *list, unsigned int nr_clk); -void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, +void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, + struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); -void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, +void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates); void rockchip_clk_protect_critical(const char *const clocks[], int nclocks); -void rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void)); +void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, + unsigned int reg, void (*cb)(void)); #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index be03ed0fcb6b..92382cef9f90 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -554,8 +554,8 @@ static struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { }; static struct samsung_div_clock exynos5800_div_clks[] __initdata = { - DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore", DIV_TOP0, 16, 3), - + DIV(CLK_DOUT_ACLK400_WCORE, "dout_aclk400_wcore", + "mout_aclk400_wcore", DIV_TOP0, 16, 3), DIV(0, "dout_aclk550_cam", "mout_aclk550_cam", DIV_TOP8, 16, 3), DIV(0, "dout_aclkfl1_550_cam", "mout_aclkfl1_550_cam", @@ -607,8 +607,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { }; static struct samsung_div_clock exynos5420_div_clks[] __initdata = { - DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll", - DIV_TOP0, 16, 3), + DIV(CLK_DOUT_ACLK400_WCORE, "dout_aclk400_wcore", + "mout_aclk400_wcore_bpll", DIV_TOP0, 16, 3), }; static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = { @@ -785,31 +785,47 @@ static struct samsung_div_clock exynos5x_div_clks[] __initdata = { DIV(0, "div_kfc", "mout_kfc", DIV_KFC0, 0, 3), DIV(0, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3), - DIV(0, "dout_aclk400_isp", "mout_aclk400_isp", DIV_TOP0, 0, 3), - DIV(0, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3), - DIV(0, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3), - DIV(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3), - DIV(0, "dout_aclk100_noc", "mout_aclk100_noc", DIV_TOP0, 20, 3), - DIV(0, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3), - DIV(0, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3), - - DIV(0, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl", - DIV_TOP1, 0, 3), - DIV(0, "dout_aclk333_432_isp", "mout_aclk333_432_isp", - DIV_TOP1, 4, 3), - DIV(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6), - DIV(0, "dout_aclk333_432_isp0", "mout_aclk333_432_isp0", - DIV_TOP1, 16, 3), - DIV(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3), - DIV(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3), - DIV(0, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3), - - DIV(0, "dout_aclk333_g2d", "mout_aclk333_g2d", DIV_TOP2, 8, 3), - DIV(0, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3), - DIV(0, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3), - DIV(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3), - DIV(0, "dout_aclk300_disp1", "mout_aclk300_disp1", DIV_TOP2, 24, 3), - DIV(0, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3), + DIV(CLK_DOUT_ACLK400_ISP, "dout_aclk400_isp", "mout_aclk400_isp", + DIV_TOP0, 0, 3), + DIV(CLK_DOUT_ACLK400_MSCL, "dout_aclk400_mscl", "mout_aclk400_mscl", + DIV_TOP0, 4, 3), + DIV(CLK_DOUT_ACLK200, "dout_aclk200", "mout_aclk200", + DIV_TOP0, 8, 3), + DIV(CLK_DOUT_ACLK200_FSYS2, "dout_aclk200_fsys2", "mout_aclk200_fsys2", + DIV_TOP0, 12, 3), + DIV(CLK_DOUT_ACLK100_NOC, "dout_aclk100_noc", "mout_aclk100_noc", + DIV_TOP0, 20, 3), + DIV(CLK_DOUT_PCLK200_FSYS, "dout_pclk200_fsys", "mout_pclk200_fsys", + DIV_TOP0, 24, 3), + DIV(CLK_DOUT_ACLK200_FSYS, "dout_aclk200_fsys", "mout_aclk200_fsys", + DIV_TOP0, 28, 3), + DIV(CLK_DOUT_ACLK333_432_GSCL, "dout_aclk333_432_gscl", + "mout_aclk333_432_gscl", DIV_TOP1, 0, 3), + DIV(CLK_DOUT_ACLK333_432_ISP, "dout_aclk333_432_isp", + "mout_aclk333_432_isp", DIV_TOP1, 4, 3), + DIV(CLK_DOUT_ACLK66, "dout_aclk66", "mout_aclk66", + DIV_TOP1, 8, 6), + DIV(CLK_DOUT_ACLK333_432_ISP0, "dout_aclk333_432_isp0", + "mout_aclk333_432_isp0", DIV_TOP1, 16, 3), + DIV(CLK_DOUT_ACLK266, "dout_aclk266", "mout_aclk266", + DIV_TOP1, 20, 3), + DIV(CLK_DOUT_ACLK166, "dout_aclk166", "mout_aclk166", + DIV_TOP1, 24, 3), + DIV(CLK_DOUT_ACLK333, "dout_aclk333", "mout_aclk333", + DIV_TOP1, 28, 3), + + DIV(CLK_DOUT_ACLK333_G2D, "dout_aclk333_g2d", "mout_aclk333_g2d", + DIV_TOP2, 8, 3), + DIV(CLK_DOUT_ACLK266_G2D, "dout_aclk266_g2d", "mout_aclk266_g2d", + DIV_TOP2, 12, 3), + DIV(CLK_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, + 16, 3), + DIV(CLK_DOUT_ACLK300_JPEG, "dout_aclk300_jpeg", "mout_aclk300_jpeg", + DIV_TOP2, 20, 3), + DIV(CLK_DOUT_ACLK300_DISP1, "dout_aclk300_disp1", + "mout_aclk300_disp1", DIV_TOP2, 24, 3), + DIV(CLK_DOUT_ACLK300_GSCL, "dout_aclk300_gscl", "mout_aclk300_gscl", + DIV_TOP2, 28, 3), /* DISP1 Block */ DIV(0, "dout_fimd1", "mout_fimd1_final", DIV_DISP10, 0, 4), @@ -817,7 +833,8 @@ static struct samsung_div_clock exynos5x_div_clks[] __initdata = { DIV(0, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4), DIV(CLK_DOUT_PIXEL, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4), DIV(0, "dout_disp1_blk", "aclk200_disp1", DIV2_RATIO0, 16, 2), - DIV(0, "dout_aclk400_disp1", "mout_aclk400_disp1", DIV_TOP2, 4, 3), + DIV(CLK_DOUT_ACLK400_DISP1, "dout_aclk400_disp1", + "mout_aclk400_disp1", DIV_TOP2, 4, 3), /* Audio Block */ DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4), diff --git a/drivers/clk/sirf/clk-atlas6.c b/drivers/clk/sirf/clk-atlas6.c index c5eaa9d16247..665fa681b2e1 100644 --- a/drivers/clk/sirf/clk-atlas6.c +++ b/drivers/clk/sirf/clk-atlas6.c @@ -130,10 +130,9 @@ static void __init atlas6_clk_init(struct device_node *np) panic("unable to map clkc registers\n"); /* These are always available (RTC and 26MHz OSC)*/ - atlas6_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, - CLK_IS_ROOT, 32768); - atlas6_clks[osc] = clk_register_fixed_rate(NULL, "osc", NULL, - CLK_IS_ROOT, 26000000); + atlas6_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, 0, 32768); + atlas6_clks[osc] = clk_register_fixed_rate(NULL, "osc", NULL, 0, + 26000000); for (i = pll1; i < maxclk; i++) { atlas6_clks[i] = clk_register(NULL, atlas6_clk_hw_array[i]); diff --git a/drivers/clk/sirf/clk-prima2.c b/drivers/clk/sirf/clk-prima2.c index f92c40264342..aac1c8ec151a 100644 --- a/drivers/clk/sirf/clk-prima2.c +++ b/drivers/clk/sirf/clk-prima2.c @@ -129,10 +129,9 @@ static void __init prima2_clk_init(struct device_node *np) panic("unable to map clkc registers\n"); /* These are always available (RTC and 26MHz OSC)*/ - prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, - CLK_IS_ROOT, 32768); - prima2_clks[osc] = clk_register_fixed_rate(NULL, "osc", NULL, - CLK_IS_ROOT, 26000000); + prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, 0, 32768); + prima2_clks[osc] = clk_register_fixed_rate(NULL, "osc", NULL, 0, + 26000000); for (i = pll1; i < maxclk; i++) { prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 3fd7901d48e4..39d2044a1f49 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -11,6 +11,9 @@ obj-y += clk-a10-ve.o obj-y += clk-a20-gmac.o obj-y += clk-mod0.o obj-y += clk-simple-gates.o +obj-y += clk-sun4i-display.o +obj-y += clk-sun4i-pll3.o +obj-y += clk-sun4i-tcon-ch1.o obj-y += clk-sun8i-bus-gates.o obj-y += clk-sun8i-mbus.o obj-y += clk-sun9i-core.o diff --git a/drivers/clk/sunxi/clk-a10-hosc.c b/drivers/clk/sunxi/clk-a10-hosc.c index 6b598c6a0213..dca532431394 100644 --- a/drivers/clk/sunxi/clk-a10-hosc.c +++ b/drivers/clk/sunxi/clk-a10-hosc.c @@ -54,8 +54,7 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) NULL, 0, NULL, NULL, &fixed->hw, &clk_fixed_rate_ops, - &gate->hw, &clk_gate_ops, - CLK_IS_ROOT); + &gate->hw, &clk_gate_ops, 0); if (IS_ERR(clk)) goto err_free_gate; diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c index e9d870de165c..e2819fa09637 100644 --- a/drivers/clk/sunxi/clk-a10-mod1.c +++ b/drivers/clk/sunxi/clk-a10-mod1.c @@ -62,7 +62,7 @@ static void __init sun4i_mod1_clk_setup(struct device_node *node) clk = clk_register_composite(NULL, clk_name, parents, i, &mux->hw, &clk_mux_ops, NULL, NULL, - &gate->hw, &clk_gate_ops, 0); + &gate->hw, &clk_gate_ops, CLK_SET_RATE_PARENT); if (IS_ERR(clk)) goto err_free_gate; diff --git a/drivers/clk/sunxi/clk-sun4i-display.c b/drivers/clk/sunxi/clk-sun4i-display.c new file mode 100644 index 000000000000..445a7498d6df --- /dev/null +++ b/drivers/clk/sunxi/clk-sun4i-display.c @@ -0,0 +1,261 @@ +/* + * Copyright 2015 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +struct sun4i_a10_display_clk_data { + bool has_div; + u8 num_rst; + u8 parents; + + u8 offset_en; + u8 offset_div; + u8 offset_mux; + u8 offset_rst; + + u8 width_div; + u8 width_mux; +}; + +struct reset_data { + void __iomem *reg; + spinlock_t *lock; + struct reset_controller_dev rcdev; + u8 offset; +}; + +static DEFINE_SPINLOCK(sun4i_a10_display_lock); + +static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct reset_data, rcdev); +}; + +static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_data *data = rcdev_to_reset_data(rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg & ~BIT(data->offset + id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_data *data = rcdev_to_reset_data(rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg | BIT(data->offset + id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static int sun4i_a10_display_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_data *data = rcdev_to_reset_data(rcdev); + + return !(readl(data->reg) & BIT(data->offset + id)); +} + +static const struct reset_control_ops sun4i_a10_display_reset_ops = { + .assert = sun4i_a10_display_assert, + .deassert = sun4i_a10_display_deassert, + .status = sun4i_a10_display_status, +}; + +static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *spec) +{ + /* We only have a single reset signal */ + return 0; +} + +static void __init sun4i_a10_display_init(struct device_node *node, + const struct sun4i_a10_display_clk_data *data) +{ + const char *parents[4]; + const char *clk_name = node->name; + struct reset_data *reset_data; + struct clk_divider *div = NULL; + struct clk_gate *gate; + struct resource res; + struct clk_mux *mux; + void __iomem *reg; + struct clk *clk; + int ret; + + of_property_read_string(node, "clock-output-names", &clk_name); + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", clk_name); + return; + } + + ret = of_clk_parent_fill(node, parents, data->parents); + if (ret != data->parents) { + pr_err("%s: Could not retrieve the parents\n", clk_name); + goto unmap; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto unmap; + + mux->reg = reg; + mux->shift = data->offset_mux; + mux->mask = (1 << data->width_mux) - 1; + mux->lock = &sun4i_a10_display_lock; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto free_mux; + + gate->reg = reg; + gate->bit_idx = data->offset_en; + gate->lock = &sun4i_a10_display_lock; + + if (data->has_div) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto free_gate; + + div->reg = reg; + div->shift = data->offset_div; + div->width = data->width_div; + div->lock = &sun4i_a10_display_lock; + } + + clk = clk_register_composite(NULL, clk_name, + parents, data->parents, + &mux->hw, &clk_mux_ops, + data->has_div ? &div->hw : NULL, + data->has_div ? &clk_divider_ops : NULL, + &gate->hw, &clk_gate_ops, + 0); + if (IS_ERR(clk)) { + pr_err("%s: Couldn't register the clock\n", clk_name); + goto free_div; + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + pr_err("%s: Couldn't register DT provider\n", clk_name); + goto free_clk; + } + + if (!data->num_rst) + return; + + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); + if (!reset_data) + goto free_of_clk; + + reset_data->reg = reg; + reset_data->offset = data->offset_rst; + reset_data->lock = &sun4i_a10_display_lock; + reset_data->rcdev.nr_resets = data->num_rst; + reset_data->rcdev.ops = &sun4i_a10_display_reset_ops; + reset_data->rcdev.of_node = node; + + if (data->num_rst == 1) { + reset_data->rcdev.of_reset_n_cells = 0; + reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate; + } else { + reset_data->rcdev.of_reset_n_cells = 1; + } + + if (reset_controller_register(&reset_data->rcdev)) { + pr_err("%s: Couldn't register the reset controller\n", + clk_name); + goto free_reset; + } + + return; + +free_reset: + kfree(reset_data); +free_of_clk: + of_clk_del_provider(node); +free_clk: + clk_unregister_composite(clk); +free_div: + kfree(div); +free_gate: + kfree(gate); +free_mux: + kfree(mux); +unmap: + iounmap(reg); + of_address_to_resource(node, 0, &res); + release_mem_region(res.start, resource_size(&res)); +} + +static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = { + .num_rst = 2, + .parents = 4, + .offset_en = 31, + .offset_rst = 29, + .offset_mux = 24, + .width_mux = 2, +}; + +static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node) +{ + sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data); +} +CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk", + sun4i_a10_tcon_ch0_setup); + +static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = { + .has_div = true, + .num_rst = 1, + .parents = 3, + .offset_en = 31, + .offset_rst = 30, + .offset_mux = 24, + .offset_div = 0, + .width_mux = 2, + .width_div = 4, +}; + +static void __init sun4i_a10_display_setup(struct device_node *node) +{ + sun4i_a10_display_init(node, &sun4i_a10_display_data); +} +CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk", + sun4i_a10_display_setup); diff --git a/drivers/clk/sunxi/clk-sun4i-pll3.c b/drivers/clk/sunxi/clk-sun4i-pll3.c new file mode 100644 index 000000000000..f66267e77d9c --- /dev/null +++ b/drivers/clk/sunxi/clk-sun4i-pll3.c @@ -0,0 +1,98 @@ +/* + * Copyright 2015 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define SUN4I_A10_PLL3_GATE_BIT 31 +#define SUN4I_A10_PLL3_DIV_WIDTH 7 +#define SUN4I_A10_PLL3_DIV_SHIFT 0 + +static DEFINE_SPINLOCK(sun4i_a10_pll3_lock); + +static void __init sun4i_a10_pll3_setup(struct device_node *node) +{ + const char *clk_name = node->name, *parent; + struct clk_multiplier *mult; + struct clk_gate *gate; + struct resource res; + void __iomem *reg; + struct clk *clk; + int ret; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent = of_clk_get_parent_name(node, 0); + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", clk_name); + return; + } + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto err_unmap; + + gate->reg = reg; + gate->bit_idx = SUN4I_A10_PLL3_GATE_BIT; + gate->lock = &sun4i_a10_pll3_lock; + + mult = kzalloc(sizeof(*mult), GFP_KERNEL); + if (!mult) + goto err_free_gate; + + mult->reg = reg; + mult->shift = SUN4I_A10_PLL3_DIV_SHIFT; + mult->width = SUN4I_A10_PLL3_DIV_WIDTH; + mult->lock = &sun4i_a10_pll3_lock; + + clk = clk_register_composite(NULL, clk_name, + &parent, 1, + NULL, NULL, + &mult->hw, &clk_multiplier_ops, + &gate->hw, &clk_gate_ops, + 0); + if (IS_ERR(clk)) { + pr_err("%s: Couldn't register the clock\n", clk_name); + goto err_free_mult; + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + pr_err("%s: Couldn't register DT provider\n", + clk_name); + goto err_clk_unregister; + } + + return; + +err_clk_unregister: + clk_unregister_composite(clk); +err_free_mult: + kfree(mult); +err_free_gate: + kfree(gate); +err_unmap: + iounmap(reg); + of_address_to_resource(node, 0, &res); + release_mem_region(res.start, resource_size(&res)); +} + +CLK_OF_DECLARE(sun4i_a10_pll3, "allwinner,sun4i-a10-pll3-clk", + sun4i_a10_pll3_setup); diff --git a/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c b/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c new file mode 100644 index 000000000000..98a4582de56a --- /dev/null +++ b/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c @@ -0,0 +1,300 @@ +/* + * Copyright 2015 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define TCON_CH1_SCLK2_PARENTS 4 + +#define TCON_CH1_SCLK2_GATE_BIT BIT(31) +#define TCON_CH1_SCLK2_MUX_MASK 3 +#define TCON_CH1_SCLK2_MUX_SHIFT 24 +#define TCON_CH1_SCLK2_DIV_MASK 0xf +#define TCON_CH1_SCLK2_DIV_SHIFT 0 + +#define TCON_CH1_SCLK1_GATE_BIT BIT(15) +#define TCON_CH1_SCLK1_HALF_BIT BIT(11) + +struct tcon_ch1_clk { + struct clk_hw hw; + spinlock_t lock; + void __iomem *reg; +}; + +#define hw_to_tclk(hw) container_of(hw, struct tcon_ch1_clk, hw) + +static void tcon_ch1_disable(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT); + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); +} + +static int tcon_ch1_enable(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT; + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); + + return 0; +} + +static int tcon_ch1_is_enabled(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + u32 reg; + + reg = readl(tclk->reg); + return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT); +} + +static u8 tcon_ch1_get_parent(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 reg; + + reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT; + reg &= reg >> TCON_CH1_SCLK2_MUX_MASK; + + if (reg >= num_parents) + return -EINVAL; + + return reg; +} + +static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT); + reg |= index << TCON_CH1_SCLK2_MUX_SHIFT; + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); + + return 0; +}; + +static unsigned long tcon_ch1_calc_divider(unsigned long rate, + unsigned long parent_rate, + u8 *div, + bool *half) +{ + unsigned long best_rate = 0; + u8 best_m = 0, m; + bool is_double; + + for (m = 1; m < 16; m++) { + u8 d; + + for (d = 1; d < 3; d++) { + unsigned long tmp_rate; + + tmp_rate = parent_rate / m / d; + + if (tmp_rate > rate) + continue; + + if (!best_rate || + (rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + best_m = m; + is_double = d; + } + } + } + + if (div && half) { + *div = best_m; + *half = is_double; + } + + return best_rate; +} + +static int tcon_ch1_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + long best_rate = -EINVAL; + int i; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + unsigned long parent_rate; + unsigned long tmp_rate; + struct clk_hw *parent; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + + tmp_rate = tcon_ch1_calc_divider(req->rate, parent_rate, + NULL, NULL); + + if (best_rate < 0 || + (req->rate - tmp_rate) < (req->rate - best_rate)) { + best_rate = tmp_rate; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + } + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + return 0; +} + +static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + u32 reg; + + reg = readl(tclk->reg); + + parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1; + + if (reg & TCON_CH1_SCLK1_HALF_BIT) + parent_rate /= 2; + + return parent_rate; +} + +static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + bool half; + u8 div_m; + u32 reg; + + tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half); + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT); + reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK; + + if (half) + reg |= TCON_CH1_SCLK1_HALF_BIT; + + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); + + return 0; +} + +static const struct clk_ops tcon_ch1_ops = { + .disable = tcon_ch1_disable, + .enable = tcon_ch1_enable, + .is_enabled = tcon_ch1_is_enabled, + + .get_parent = tcon_ch1_get_parent, + .set_parent = tcon_ch1_set_parent, + + .determine_rate = tcon_ch1_determine_rate, + .recalc_rate = tcon_ch1_recalc_rate, + .set_rate = tcon_ch1_set_rate, +}; + +static void __init tcon_ch1_setup(struct device_node *node) +{ + const char *parents[TCON_CH1_SCLK2_PARENTS]; + const char *clk_name = node->name; + struct clk_init_data init; + struct tcon_ch1_clk *tclk; + struct resource res; + struct clk *clk; + void __iomem *reg; + int ret; + + of_property_read_string(node, "clock-output-names", &clk_name); + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", clk_name); + return; + } + + ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS); + if (ret != TCON_CH1_SCLK2_PARENTS) { + pr_err("%s Could not retrieve the parents\n", clk_name); + goto err_unmap; + } + + tclk = kzalloc(sizeof(*tclk), GFP_KERNEL); + if (!tclk) + goto err_unmap; + + init.name = clk_name; + init.ops = &tcon_ch1_ops; + init.parent_names = parents; + init.num_parents = TCON_CH1_SCLK2_PARENTS; + init.flags = CLK_SET_RATE_PARENT; + + tclk->reg = reg; + tclk->hw.init = &init; + spin_lock_init(&tclk->lock); + + clk = clk_register(NULL, &tclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: Couldn't register the clock\n", clk_name); + goto err_free_data; + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + pr_err("%s: Couldn't register our clock provider\n", clk_name); + goto err_unregister_clk; + } + + return; + +err_unregister_clk: + clk_unregister(clk); +err_free_data: + kfree(tclk); +err_unmap: + iounmap(reg); + of_address_to_resource(node, 0, &res); + release_mem_region(res.start, resource_size(&res)); +} + +CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk", + tcon_ch1_setup); diff --git a/drivers/clk/sunxi/clk-sun9i-mmc.c b/drivers/clk/sunxi/clk-sun9i-mmc.c index 028dd832a39f..716737388b7d 100644 --- a/drivers/clk/sunxi/clk-sun9i-mmc.c +++ b/drivers/clk/sunxi/clk-sun9i-mmc.c @@ -106,7 +106,7 @@ static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* one clock/reset pair per word */ - count = DIV_ROUND_UP((r->end - r->start + 1), SUN9I_MMC_WIDTH); + count = DIV_ROUND_UP((resource_size(r)), SUN9I_MMC_WIDTH); data->membase = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(data->membase)) return PTR_ERR(data->membase); diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 91de0a006773..838b22aa8b67 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -523,21 +523,12 @@ static const struct factors_data sun4i_pll5_data __initconst = { .enable = 31, .table = &sun4i_pll5_config, .getter = sun4i_get_pll5_factors, - .name = "pll5", -}; - -static const struct factors_data sun4i_pll6_data __initconst = { - .enable = 31, - .table = &sun4i_pll5_config, - .getter = sun4i_get_pll5_factors, - .name = "pll6", }; static const struct factors_data sun6i_a31_pll6_data __initconst = { .enable = 31, .table = &sun6i_a31_pll6_config, .getter = sun6i_a31_get_pll6_factors, - .name = "pll6x2", }; static const struct factors_data sun5i_a13_ahb_data __initconst = { @@ -933,7 +924,7 @@ static const struct divs_data pll5_divs_data __initconst = { }; static const struct divs_data pll6_divs_data __initconst = { - .factors = &sun4i_pll6_data, + .factors = &sun4i_pll5_data, .ndivs = 4, .div = { { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ @@ -975,6 +966,8 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, struct clk_gate *gate = NULL; struct clk_fixed_factor *fix_factor; struct clk_divider *divider; + struct factors_data factors = *data->factors; + char *derived_name = NULL; void __iomem *reg; int ndivs = SUNXI_DIVS_MAX_QTY, i = 0; int flags, clkflags; @@ -983,11 +976,37 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, if (data->ndivs) ndivs = data->ndivs; + /* Try to find a name for base factor clock */ + for (i = 0; i < ndivs; i++) { + if (data->div[i].self) { + of_property_read_string_index(node, "clock-output-names", + i, &factors.name); + break; + } + } + /* If we don't have a .self clk use the first output-name up to '_' */ + if (factors.name == NULL) { + char *endp; + + of_property_read_string_index(node, "clock-output-names", + 0, &clk_name); + endp = strchr(clk_name, '_'); + if (endp) { + derived_name = kstrndup(clk_name, endp - clk_name, + GFP_KERNEL); + factors.name = derived_name; + } else { + factors.name = clk_name; + } + } + /* Set up factor clock that we will be dividing */ - pclk = sunxi_factors_clk_setup(node, data->factors); + pclk = sunxi_factors_clk_setup(node, &factors); if (!pclk) return NULL; + parent = __clk_get_name(pclk); + kfree(derived_name); reg = of_iomap(node, 0); if (!reg) { @@ -1127,3 +1146,41 @@ static void __init sun6i_pll6_clk_setup(struct device_node *node) } CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk", sun6i_pll6_clk_setup); + +/* + * sun6i display + * + * rate = parent_rate / (m + 1); + */ +static void sun6i_display_factors(struct factors_request *req) +{ + u8 m; + + if (req->rate > req->parent_rate) + req->rate = req->parent_rate; + + m = DIV_ROUND_UP(req->parent_rate, req->rate); + + req->rate = req->parent_rate / m; + req->m = m - 1; +} + +static const struct clk_factors_config sun6i_display_config = { + .mshift = 0, + .mwidth = 4, +}; + +static const struct factors_data sun6i_display_data __initconst = { + .enable = 31, + .mux = 24, + .muxmask = BIT(2) | BIT(1) | BIT(0), + .table = &sun6i_display_config, + .getter = sun6i_display_factors, +}; + +static void __init sun6i_display_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun6i_display_data); +} +CLK_OF_DECLARE(sun6i_display, "allwinner,sun6i-a31-display-clk", + sun6i_display_setup); diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 97984c503bbb..33fd0938d79e 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -3,6 +3,7 @@ obj-y += clk-audio-sync.o obj-y += clk-dfll.o obj-y += clk-divider.o obj-y += clk-periph.o +obj-y += clk-periph-fixed.o obj-y += clk-periph-gate.o obj-y += clk-pll.o obj-y += clk-pll-out.o diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index 19bfa07e24b1..f010562534eb 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -55,6 +55,7 @@ #include <linux/seq_file.h> #include "clk-dfll.h" +#include "cvb.h" /* * DFLL control registers - access via dfll_{readl,writel} @@ -442,8 +443,8 @@ static void dfll_tune_low(struct tegra_dfll *td) { td->tune_range = DFLL_TUNE_LOW; - dfll_writel(td, td->soc->tune0_low, DFLL_TUNE0); - dfll_writel(td, td->soc->tune1, DFLL_TUNE1); + dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune0_low, DFLL_TUNE0); + dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune1, DFLL_TUNE1); dfll_wmb(td); if (td->soc->set_clock_trimmers_low) @@ -1449,7 +1450,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td) } v_max = dev_pm_opp_get_voltage(opp); - v = td->soc->min_millivolts * 1000; + v = td->soc->cvb->min_millivolts * 1000; lut = find_vdd_map_entry_exact(td, v); if (lut < 0) goto out; @@ -1461,7 +1462,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td) break; v_opp = dev_pm_opp_get_voltage(opp); - if (v_opp <= td->soc->min_millivolts * 1000) + if (v_opp <= td->soc->cvb->min_millivolts * 1000) td->dvco_rate_min = dev_pm_opp_get_freq(opp); for (;;) { @@ -1490,7 +1491,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td) if (!td->dvco_rate_min) dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n", - td->soc->min_millivolts); + td->soc->cvb->min_millivolts); else ret = 0; diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h index 2e4c0772a5dc..ed2ad888268f 100644 --- a/drivers/clk/tegra/clk-dfll.h +++ b/drivers/clk/tegra/clk-dfll.h @@ -24,22 +24,18 @@ /** * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver - * @opp_dev: struct device * that holds the OPP table for the DFLL - * @min_millivolts: minimum voltage (in mV) that the DFLL can operate - * @tune0_low: DFLL tuning register 0 (low voltage range) - * @tune0_high: DFLL tuning register 0 (high voltage range) - * @tune1: DFLL tuning register 1 - * @assert_dvco_reset: fn ptr to place the DVCO in reset - * @deassert_dvco_reset: fn ptr to release the DVCO reset - * @set_clock_trimmers_high: fn ptr to tune clock trimmers for high voltage - * @set_clock_trimmers_low: fn ptr to tune clock trimmers for low voltage + * @dev: struct device * that holds the OPP table for the DFLL + * @max_freq: maximum frequency supported on this SoC + * @cvb: CPU frequency table for this SoC + * @init_clock_trimmers: callback to initialize clock trimmers + * @set_clock_trimmers_high: callback to tune clock trimmers for high voltage + * @set_clock_trimmers_low: callback to tune clock trimmers for low voltage */ struct tegra_dfll_soc_data { struct device *dev; - unsigned int min_millivolts; - u32 tune0_low; - u32 tune0_high; - u32 tune1; + unsigned long max_freq; + const struct cvb_table *cvb; + void (*init_clock_trimmers)(void); void (*set_clock_trimmers_high)(void); void (*set_clock_trimmers_low)(void); diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index 62ea38187b71..36c974916d4f 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -71,6 +71,7 @@ enum clk_id { tegra_clk_disp2_8, tegra_clk_dp2, tegra_clk_dpaux, + tegra_clk_dpaux1, tegra_clk_dsialp, tegra_clk_dsia_mux, tegra_clk_dsiblp, @@ -306,6 +307,7 @@ enum clk_id { tegra_clk_xusb_ss_div2, tegra_clk_xusb_ssp_src, tegra_clk_sclk_mux, + tegra_clk_sor_safe, tegra_clk_max, }; diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c new file mode 100644 index 000000000000..c57dfb037b10 --- /dev/null +++ b/drivers/clk/tegra/clk-periph-fixed.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk-provider.h> + +#include "clk.h" + +static inline struct tegra_clk_periph_fixed * +to_tegra_clk_periph_fixed(struct clk_hw *hw) +{ + return container_of(hw, struct tegra_clk_periph_fixed, hw); +} + +static int tegra_clk_periph_fixed_is_enabled(struct clk_hw *hw) +{ + struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); + u32 mask = 1 << (fixed->num % 32), value; + + value = readl(fixed->base + fixed->regs->enb_reg); + if (value & mask) { + value = readl(fixed->base + fixed->regs->rst_reg); + if ((value & mask) == 0) + return 1; + } + + return 0; +} + +static int tegra_clk_periph_fixed_enable(struct clk_hw *hw) +{ + struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); + u32 mask = 1 << (fixed->num % 32); + + writel(mask, fixed->base + fixed->regs->enb_set_reg); + + return 0; +} + +static void tegra_clk_periph_fixed_disable(struct clk_hw *hw) +{ + struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); + u32 mask = 1 << (fixed->num % 32); + + writel(mask, fixed->base + fixed->regs->enb_clr_reg); +} + +static unsigned long +tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); + unsigned long long rate; + + rate = (unsigned long long)parent_rate * fixed->mul; + do_div(rate, fixed->div); + + return (unsigned long)rate; +} + +static const struct clk_ops tegra_clk_periph_fixed_ops = { + .is_enabled = tegra_clk_periph_fixed_is_enabled, + .enable = tegra_clk_periph_fixed_enable, + .disable = tegra_clk_periph_fixed_disable, + .recalc_rate = tegra_clk_periph_fixed_recalc_rate, +}; + +struct clk *tegra_clk_register_periph_fixed(const char *name, + const char *parent, + unsigned long flags, + void __iomem *base, + unsigned int mul, + unsigned int div, + unsigned int num) +{ + const struct tegra_clk_periph_regs *regs; + struct tegra_clk_periph_fixed *fixed; + struct clk_init_data init; + struct clk *clk; + + regs = get_reg_bank(num); + if (!regs) + return ERR_PTR(-EINVAL); + + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = flags; + init.parent_names = parent ? &parent : NULL; + init.num_parents = parent ? 1 : 0; + init.ops = &tegra_clk_periph_fixed_ops; + + fixed->base = base; + fixed->regs = regs; + fixed->mul = mul; + fixed->div = div; + fixed->num = num; + + fixed->hw.init = &init; + + clk = clk_register(NULL, &fixed->hw); + if (IS_ERR(clk)) + kfree(fixed); + + return clk; +} diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index d28d6e95020f..88127828befe 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -134,7 +134,7 @@ struct clk *tegra_clk_register_periph_gate(const char *name, struct tegra_clk_periph_gate *gate; struct clk *clk; struct clk_init_data init; - struct tegra_clk_periph_regs *pregs; + const struct tegra_clk_periph_regs *pregs; pregs = get_reg_bank(clk_num); if (!pregs) diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index ec5b6113b012..a17ca6d7f649 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -145,7 +145,7 @@ static struct clk *_tegra_clk_register_periph(const char *name, { struct clk *clk; struct clk_init_data init; - struct tegra_clk_periph_regs *bank; + const struct tegra_clk_periph_regs *bank; bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV); if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) { diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 6ac3f843e7ca..4e194ecc8d5e 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -2013,6 +2013,52 @@ struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name, #endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) +struct clk *tegra_clk_register_pllre_tegra210(const char *name, + const char *parent_name, void __iomem *clk_base, + void __iomem *pmc, unsigned long flags, + struct tegra_clk_pll_params *pll_params, + spinlock_t *lock, unsigned long parent_rate) +{ + u32 val; + struct tegra_clk_pll *pll; + struct clk *clk; + + pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate); + + if (pll_params->adjust_vco) + pll_params->vco_min = pll_params->adjust_vco(pll_params, + parent_rate); + + pll = _tegra_init_pll(clk_base, pmc, pll_params, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* program minimum rate by default */ + + val = pll_readl_base(pll); + if (val & PLL_BASE_ENABLE) + WARN_ON(readl_relaxed(clk_base + pll_params->iddq_reg) & + BIT(pll_params->iddq_bit_idx)); + else { + val = 0x4 << divm_shift(pll); + val |= 0x41 << divn_shift(pll); + pll_writel_base(val, pll); + } + + /* disable lock override */ + + val = pll_readl_misc(pll); + val &= ~BIT(29); + pll_writel_misc(val, pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllre_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + static int clk_plle_tegra210_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c index d64ec7a1b976..91c38f1666c1 100644 --- a/drivers/clk/tegra/clk-tegra-fixed.c +++ b/drivers/clk/tegra/clk-tegra-fixed.c @@ -107,4 +107,3 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks) *dt_clk = clk; } } - diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index ea2b9cbf9e70..29d04c663abf 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -803,7 +803,7 @@ static struct tegra_periph_init_data gate_clks[] = { GATE("hda2hdmi", "clk_m", 128, TEGRA_PERIPH_ON_APB, tegra_clk_hda2hdmi, 0), GATE("bsea", "clk_m", 62, 0, tegra_clk_bsea, 0), GATE("bsev", "clk_m", 63, 0, tegra_clk_bsev, 0), - GATE("mipi-cal", "clk_m", 56, 0, tegra_clk_mipi_cal, 0), + GATE("mipi-cal", "clk72mhz", 56, 0, tegra_clk_mipi_cal, 0), GATE("usbd", "clk_m", 22, 0, tegra_clk_usbd, 0), GATE("usb2", "clk_m", 58, 0, tegra_clk_usb2, 0), GATE("usb3", "clk_m", 59, 0, tegra_clk_usb3, 0), @@ -821,7 +821,6 @@ static struct tegra_periph_init_data gate_clks[] = { GATE("ispb", "clk_m", 3, 0, tegra_clk_ispb, 0), GATE("vim2_clk", "clk_m", 11, 0, tegra_clk_vim2_clk, 0), GATE("pcie", "clk_m", 70, 0, tegra_clk_pcie, 0), - GATE("dpaux", "clk_m", 181, 0, tegra_clk_dpaux, 0), GATE("gpu", "pll_ref", 184, 0, tegra_clk_gpu, 0), GATE("pllg_ref", "pll_ref", 189, 0, tegra_clk_pll_g_ref, 0), GATE("hsic_trk", "usb2_hsic_trk", 209, TEGRA_PERIPH_NO_RESET, tegra_clk_hsic_trk, 0), @@ -877,7 +876,7 @@ static void __init periph_clk_init(void __iomem *clk_base, struct clk **dt_clk; for (i = 0; i < ARRAY_SIZE(periph_clks); i++) { - struct tegra_clk_periph_regs *bank; + const struct tegra_clk_periph_regs *bank; struct tegra_periph_init_data *data; data = periph_clks + i; diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index df47ec3169c3..b78054fac0a8 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -743,7 +743,6 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_csi] = { .dt_id = TEGRA114_CLK_CSI, .present = true }, [tegra_clk_i2c2] = { .dt_id = TEGRA114_CLK_I2C2, .present = true }, [tegra_clk_uartc] = { .dt_id = TEGRA114_CLK_UARTC, .present = true }, - [tegra_clk_mipi_cal] = { .dt_id = TEGRA114_CLK_MIPI_CAL, .present = true }, [tegra_clk_emc] = { .dt_id = TEGRA114_CLK_EMC, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA114_CLK_USB2, .present = true }, [tegra_clk_usb3] = { .dt_id = TEGRA114_CLK_USB3, .present = true }, @@ -1237,6 +1236,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, &emc_lock); clks[TEGRA114_CLK_MC] = clk; + clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base, + CLK_SET_RATE_PARENT, 56, + periph_clk_enb_refcnt); + clks[TEGRA114_CLK_MIPI_CAL] = clk; + for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c index 61253330c12b..c205809ba580 100644 --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c @@ -47,32 +47,32 @@ static const struct cvb_table tegra124_cpu_cvb_tables[] = { }, .speedo_scale = 100, .voltage_scale = 1000, - .cvb_table = { - {204000000UL, {1112619, -29295, 402} }, - {306000000UL, {1150460, -30585, 402} }, - {408000000UL, {1190122, -31865, 402} }, - {510000000UL, {1231606, -33155, 402} }, - {612000000UL, {1274912, -34435, 402} }, - {714000000UL, {1320040, -35725, 402} }, - {816000000UL, {1366990, -37005, 402} }, - {918000000UL, {1415762, -38295, 402} }, - {1020000000UL, {1466355, -39575, 402} }, - {1122000000UL, {1518771, -40865, 402} }, - {1224000000UL, {1573009, -42145, 402} }, - {1326000000UL, {1629068, -43435, 402} }, - {1428000000UL, {1686950, -44715, 402} }, - {1530000000UL, {1746653, -46005, 402} }, - {1632000000UL, {1808179, -47285, 402} }, - {1734000000UL, {1871526, -48575, 402} }, - {1836000000UL, {1936696, -49855, 402} }, - {1938000000UL, {2003687, -51145, 402} }, - {2014500000UL, {2054787, -52095, 402} }, - {2116500000UL, {2124957, -53385, 402} }, - {2218500000UL, {2196950, -54665, 402} }, - {2320500000UL, {2270765, -55955, 402} }, - {2422500000UL, {2346401, -57235, 402} }, - {2524500000UL, {2437299, -58535, 402} }, - {0, { 0, 0, 0} }, + .entries = { + { 204000000UL, { 1112619, -29295, 402 } }, + { 306000000UL, { 1150460, -30585, 402 } }, + { 408000000UL, { 1190122, -31865, 402 } }, + { 510000000UL, { 1231606, -33155, 402 } }, + { 612000000UL, { 1274912, -34435, 402 } }, + { 714000000UL, { 1320040, -35725, 402 } }, + { 816000000UL, { 1366990, -37005, 402 } }, + { 918000000UL, { 1415762, -38295, 402 } }, + { 1020000000UL, { 1466355, -39575, 402 } }, + { 1122000000UL, { 1518771, -40865, 402 } }, + { 1224000000UL, { 1573009, -42145, 402 } }, + { 1326000000UL, { 1629068, -43435, 402 } }, + { 1428000000UL, { 1686950, -44715, 402 } }, + { 1530000000UL, { 1746653, -46005, 402 } }, + { 1632000000UL, { 1808179, -47285, 402 } }, + { 1734000000UL, { 1871526, -48575, 402 } }, + { 1836000000UL, { 1936696, -49855, 402 } }, + { 1938000000UL, { 2003687, -51145, 402 } }, + { 2014500000UL, { 2054787, -52095, 402 } }, + { 2116500000UL, { 2124957, -53385, 402 } }, + { 2218500000UL, { 2196950, -54665, 402 } }, + { 2320500000UL, { 2270765, -55955, 402 } }, + { 2422500000UL, { 2346401, -57235, 402 } }, + { 2524500000UL, { 2437299, -58535, 402 } }, + { 0UL, { 0, 0, 0 } }, }, .cpu_dfll_data = { .tune0_low = 0x005020ff, @@ -84,9 +84,8 @@ static const struct cvb_table tegra124_cpu_cvb_tables[] = { static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) { - int process_id, speedo_id, speedo_value; + int process_id, speedo_id, speedo_value, err; struct tegra_dfll_soc_data *soc; - const struct cvb_table *cvb; process_id = tegra_sku_info.cpu_process_id; speedo_id = tegra_sku_info.cpu_speedo_id; @@ -108,23 +107,41 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) return -ENODEV; } - cvb = tegra_cvb_build_opp_table(tegra124_cpu_cvb_tables, - ARRAY_SIZE(tegra124_cpu_cvb_tables), - process_id, speedo_id, speedo_value, - cpu_max_freq_table[speedo_id], - soc->dev); - if (IS_ERR(cvb)) { - dev_err(&pdev->dev, "couldn't build OPP table: %ld\n", - PTR_ERR(cvb)); - return PTR_ERR(cvb); + soc->max_freq = cpu_max_freq_table[speedo_id]; + + soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables, + ARRAY_SIZE(tegra124_cpu_cvb_tables), + process_id, speedo_id, speedo_value, + soc->max_freq); + if (IS_ERR(soc->cvb)) { + dev_err(&pdev->dev, "couldn't add OPP table: %ld\n", + PTR_ERR(soc->cvb)); + return PTR_ERR(soc->cvb); + } + + err = tegra_dfll_register(pdev, soc); + if (err < 0) { + tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq); + return err; } - soc->min_millivolts = cvb->min_millivolts; - soc->tune0_low = cvb->cpu_dfll_data.tune0_low; - soc->tune0_high = cvb->cpu_dfll_data.tune0_high; - soc->tune1 = cvb->cpu_dfll_data.tune1; + platform_set_drvdata(pdev, soc); + + return 0; +} + +static int tegra124_dfll_fcpu_remove(struct platform_device *pdev) +{ + struct tegra_dfll_soc_data *soc = platform_get_drvdata(pdev); + int err; + + err = tegra_dfll_unregister(pdev); + if (err < 0) + dev_err(&pdev->dev, "failed to unregister DFLL: %d\n", err); + + tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq); - return tegra_dfll_register(pdev, soc); + return 0; } static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { @@ -140,7 +157,7 @@ static const struct dev_pm_ops tegra124_dfll_pm_ops = { static struct platform_driver tegra124_dfll_fcpu_driver = { .probe = tegra124_dfll_fcpu_probe, - .remove = tegra_dfll_unregister, + .remove = tegra124_dfll_fcpu_remove, .driver = { .name = "tegra124-dfll", .of_match_table = tegra124_dfll_fcpu_of_match, diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 1627258292d2..f4fbbf16a056 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1155,6 +1155,10 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, 1, 2); clks[TEGRA124_CLK_XUSB_SS_DIV2] = clk; + clk = tegra_clk_register_periph_fixed("dpaux", "pll_p", 0, clk_base, + 1, 17, 181); + clks[TEGRA124_CLK_DPAUX] = clk; + clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0, clk_base + PLLD_MISC, 30, 0, &pll_d_lock); clks[TEGRA124_CLK_PLL_D_DSI_OUT] = clk; diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 3d0edee1f9fe..b8551813ec43 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -92,6 +92,7 @@ #define PLLE_AUX 0x48c #define PLLRE_BASE 0x4c4 #define PLLRE_MISC0 0x4c8 +#define PLLRE_OUT1 0x4cc #define PLLDP_BASE 0x590 #define PLLDP_MISC 0x594 @@ -2150,6 +2151,7 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { [tegra_clk_clk72Mhz_8] = { .dt_id = TEGRA210_CLK_CLK72MHZ, .present = true }, [tegra_clk_vic03_8] = { .dt_id = TEGRA210_CLK_VIC03, .present = true }, [tegra_clk_dpaux] = { .dt_id = TEGRA210_CLK_DPAUX, .present = true }, + [tegra_clk_dpaux1] = { .dt_id = TEGRA210_CLK_DPAUX1, .present = true }, [tegra_clk_sor0] = { .dt_id = TEGRA210_CLK_SOR0, .present = true }, [tegra_clk_sor0_lvds] = { .dt_id = TEGRA210_CLK_SOR0_LVDS, .present = true }, [tegra_clk_gpu] = { .dt_id = TEGRA210_CLK_GPU, .present = true }, @@ -2461,6 +2463,18 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, 1, 2); clks[TEGRA210_CLK_XUSB_SS_DIV2] = clk; + clk = tegra_clk_register_periph_fixed("dpaux", "pll_p", 0, clk_base, + 1, 17, 181); + clks[TEGRA210_CLK_DPAUX] = clk; + + clk = tegra_clk_register_periph_fixed("dpaux1", "pll_p", 0, clk_base, + 1, 17, 207); + clks[TEGRA210_CLK_DPAUX1] = clk; + + clk = tegra_clk_register_periph_fixed("sor_safe", "pll_p", 0, clk_base, + 1, 17, 222); + clks[TEGRA210_CLK_SOR_SAFE] = clk; + /* pll_d_dsi_out */ clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0, clk_base + PLLD_MISC0, 21, 0, &pll_d_lock); @@ -2640,8 +2654,10 @@ static void __init tegra210_pll_init(void __iomem *clk_base, clks[TEGRA210_CLK_PLL_D_OUT0] = clk; /* PLLRE */ - clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc, - 0, &pll_re_vco_params, &pll_re_lock, pll_ref_freq); + clk = tegra_clk_register_pllre_tegra210("pll_re_vco", "pll_ref", + clk_base, pmc, 0, + &pll_re_vco_params, + &pll_re_lock, pll_ref_freq); clk_register_clkdev(clk, "pll_re_vco", NULL); clks[TEGRA210_CLK_PLL_RE_VCO] = clk; @@ -2651,6 +2667,15 @@ static void __init tegra210_pll_init(void __iomem *clk_base, clk_register_clkdev(clk, "pll_re_out", NULL); clks[TEGRA210_CLK_PLL_RE_OUT] = clk; + clk = tegra_clk_register_divider("pll_re_out1_div", "pll_re_vco", + clk_base + PLLRE_OUT1, 0, + TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_re_out1", "pll_re_out1_div", + clk_base + PLLRE_OUT1, 1, 0, + CLK_SET_RATE_PARENT, 0, NULL); + clks[TEGRA210_CLK_PLL_RE_OUT1] = clk; + /* PLLE */ clk = tegra_clk_register_plle_tegra210("pll_e", "pll_ref", clk_base, 0, &pll_e_params, NULL); diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 0478565cf292..9396f4930da7 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -339,11 +339,11 @@ static const struct pdiv_map pllu_p[] = { }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 1, 12 }, - { 13000000, 480000000, 960, 13, 1, 12 }, - { 16800000, 480000000, 400, 7, 1, 5 }, - { 19200000, 480000000, 200, 4, 1, 3 }, - { 26000000, 480000000, 960, 26, 1, 12 }, + { 12000000, 480000000, 960, 12, 2, 12 }, + { 13000000, 480000000, 960, 13, 2, 12 }, + { 16800000, 480000000, 400, 7, 2, 5 }, + { 19200000, 480000000, 200, 4, 2, 3 }, + { 26000000, 480000000, 960, 26, 2, 12 }, { 0, 0, 0, 0, 0, 0 }, }; @@ -1372,6 +1372,7 @@ static struct tegra_clk_init_table init_table[] __initdata = { { TEGRA30_CLK_SBC4, TEGRA30_CLK_PLL_P, 100000000, 0 }, { TEGRA30_CLK_SBC5, TEGRA30_CLK_PLL_P, 100000000, 0 }, { TEGRA30_CLK_SBC6, TEGRA30_CLK_PLL_P, 100000000, 0 }, + { TEGRA30_CLK_PLL_C, TEGRA30_CLK_CLK_MAX, 600000000, 0 }, { TEGRA30_CLK_HOST1X, TEGRA30_CLK_PLL_C, 150000000, 0 }, { TEGRA30_CLK_DISP1, TEGRA30_CLK_PLL_P, 600000000, 0 }, { TEGRA30_CLK_DISP2, TEGRA30_CLK_PLL_P, 600000000, 0 }, @@ -1379,6 +1380,7 @@ static struct tegra_clk_init_table init_table[] __initdata = { { TEGRA30_CLK_GR2D, TEGRA30_CLK_PLL_C, 300000000, 0 }, { TEGRA30_CLK_GR3D, TEGRA30_CLK_PLL_C, 300000000, 0 }, { TEGRA30_CLK_GR3D2, TEGRA30_CLK_PLL_C, 300000000, 0 }, + { TEGRA30_CLK_PLL_U, TEGRA30_CLK_CLK_MAX, 480000000, 0 }, /* must be the last entry */ { TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index f60fe2e344ca..b2cdd9a235f4 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -84,7 +84,7 @@ static int (*special_reset_assert)(unsigned long); static int (*special_reset_deassert)(unsigned long); static unsigned int num_special_reset; -static struct tegra_clk_periph_regs periph_regs[] = { +static const struct tegra_clk_periph_regs periph_regs[] = { [0] = { .enb_reg = CLK_OUT_ENB_L, .enb_set_reg = CLK_OUT_ENB_SET_L, @@ -182,7 +182,7 @@ static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev, return -EINVAL; } -struct tegra_clk_periph_regs *get_reg_bank(int clkid) +const struct tegra_clk_periph_regs *get_reg_bank(int clkid) { int reg_bank = clkid / 32; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 4dbcfaec576a..9421f0310999 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -386,6 +386,12 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, struct tegra_clk_pll_params *pll_params, spinlock_t *lock, unsigned long parent_rate); +struct clk *tegra_clk_register_pllre_tegra210(const char *name, + const char *parent_name, void __iomem *clk_base, + void __iomem *pmc, unsigned long flags, + struct tegra_clk_pll_params *pll_params, + spinlock_t *lock, unsigned long parent_rate); + struct clk *tegra_clk_register_plle_tegra114(const char *name, const char *parent_name, void __iomem *clk_base, unsigned long flags, @@ -496,7 +502,7 @@ struct tegra_clk_periph_gate { u8 flags; int clk_num; int *enable_refcnt; - struct tegra_clk_periph_regs *regs; + const struct tegra_clk_periph_regs *regs; }; #define to_clk_periph_gate(_hw) \ @@ -516,6 +522,23 @@ struct clk *tegra_clk_register_periph_gate(const char *name, const char *parent_name, u8 gate_flags, void __iomem *clk_base, unsigned long flags, int clk_num, int *enable_refcnt); +struct tegra_clk_periph_fixed { + struct clk_hw hw; + void __iomem *base; + const struct tegra_clk_periph_regs *regs; + unsigned int mul; + unsigned int div; + unsigned int num; +}; + +struct clk *tegra_clk_register_periph_fixed(const char *name, + const char *parent, + unsigned long flags, + void __iomem *base, + unsigned int mul, + unsigned int div, + unsigned int num); + /** * struct clk-periph - peripheral clock * @@ -716,7 +739,7 @@ void tegra_init_from_table(struct tegra_clk_init_table *tbl, void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max); -struct tegra_clk_periph_regs *get_reg_bank(int clkid); +const struct tegra_clk_periph_regs *get_reg_bank(int clkid); struct clk **tegra_clk_init(void __iomem *clk_base, int num, int periph_banks); struct clk **tegra_lookup_dt_id(int clk_id, struct tegra_clk *tegra_clk); diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c index 69c74eec3a4b..624115e82ff9 100644 --- a/drivers/clk/tegra/cvb.c +++ b/drivers/clk/tegra/cvb.c @@ -61,29 +61,28 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up) return mv; } -static int build_opp_table(const struct cvb_table *d, - int speedo_value, - unsigned long max_freq, - struct device *opp_dev) +static int build_opp_table(struct device *dev, const struct cvb_table *table, + int speedo_value, unsigned long max_freq) { + const struct rail_alignment *align = &table->alignment; int i, ret, dfll_mv, min_mv, max_mv; - const struct cvb_table_freq_entry *table = NULL; - const struct rail_alignment *align = &d->alignment; - min_mv = round_voltage(d->min_millivolts, align, UP); - max_mv = round_voltage(d->max_millivolts, align, DOWN); + min_mv = round_voltage(table->min_millivolts, align, UP); + max_mv = round_voltage(table->max_millivolts, align, DOWN); for (i = 0; i < MAX_DVFS_FREQS; i++) { - table = &d->cvb_table[i]; - if (!table->freq || (table->freq > max_freq)) + const struct cvb_table_freq_entry *entry = &table->entries[i]; + + if (!entry->freq || (entry->freq > max_freq)) break; - dfll_mv = get_cvb_voltage( - speedo_value, d->speedo_scale, &table->coefficients); - dfll_mv = round_cvb_voltage(dfll_mv, d->voltage_scale, align); + dfll_mv = get_cvb_voltage(speedo_value, table->speedo_scale, + &entry->coefficients); + dfll_mv = round_cvb_voltage(dfll_mv, table->voltage_scale, + align); dfll_mv = clamp(dfll_mv, min_mv, max_mv); - ret = dev_pm_opp_add(opp_dev, table->freq, dfll_mv * 1000); + ret = dev_pm_opp_add(dev, entry->freq, dfll_mv * 1000); if (ret) return ret; } @@ -92,7 +91,7 @@ static int build_opp_table(const struct cvb_table *d, } /** - * tegra_cvb_build_opp_table - build OPP table from Tegra CVB tables + * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables * @cvb_tables: array of CVB tables * @sz: size of the previously mentioned array * @process_id: process id of the HW module @@ -108,26 +107,42 @@ static int build_opp_table(const struct cvb_table *d, * given @opp_dev. Returns a pointer to the struct cvb_table that matched * or an ERR_PTR on failure. */ -const struct cvb_table *tegra_cvb_build_opp_table( - const struct cvb_table *cvb_tables, - size_t sz, int process_id, - int speedo_id, int speedo_value, - unsigned long max_rate, - struct device *opp_dev) +const struct cvb_table * +tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables, + size_t count, int process_id, int speedo_id, + int speedo_value, unsigned long max_freq) { - int i, ret; + size_t i; + int ret; - for (i = 0; i < sz; i++) { - const struct cvb_table *d = &cvb_tables[i]; + for (i = 0; i < count; i++) { + const struct cvb_table *table = &tables[i]; - if (d->speedo_id != -1 && d->speedo_id != speedo_id) + if (table->speedo_id != -1 && table->speedo_id != speedo_id) continue; - if (d->process_id != -1 && d->process_id != process_id) + + if (table->process_id != -1 && table->process_id != process_id) continue; - ret = build_opp_table(d, speedo_value, max_rate, opp_dev); - return ret ? ERR_PTR(ret) : d; + ret = build_opp_table(dev, table, speedo_value, max_freq); + return ret ? ERR_PTR(ret) : table; } return ERR_PTR(-EINVAL); } + +void tegra_cvb_remove_opp_table(struct device *dev, + const struct cvb_table *table, + unsigned long max_freq) +{ + unsigned int i; + + for (i = 0; i < MAX_DVFS_FREQS; i++) { + const struct cvb_table_freq_entry *entry = &table->entries[i]; + + if (!entry->freq || (entry->freq > max_freq)) + break; + + dev_pm_opp_remove(dev, entry->freq); + } +} diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h index f62cdc4f4234..c1f077993b2a 100644 --- a/drivers/clk/tegra/cvb.h +++ b/drivers/clk/tegra/cvb.h @@ -53,15 +53,16 @@ struct cvb_table { int speedo_scale; int voltage_scale; - struct cvb_table_freq_entry cvb_table[MAX_DVFS_FREQS]; + struct cvb_table_freq_entry entries[MAX_DVFS_FREQS]; struct cvb_cpu_dfll_data cpu_dfll_data; }; -const struct cvb_table *tegra_cvb_build_opp_table( - const struct cvb_table *cvb_tables, - size_t sz, int process_id, - int speedo_id, int speedo_value, - unsigned long max_rate, - struct device *opp_dev); +const struct cvb_table * +tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables, + size_t count, int process_id, int speedo_id, + int speedo_value, unsigned long max_freq); +void tegra_cvb_remove_opp_table(struct device *dev, + const struct cvb_table *table, + unsigned long max_freq); #endif diff --git a/drivers/clk/ti/clk-54xx.c b/drivers/clk/ti/clk-54xx.c index 59ce2fa2c104..294bc03ec067 100644 --- a/drivers/clk/ti/clk-54xx.c +++ b/drivers/clk/ti/clk-54xx.c @@ -210,6 +210,7 @@ static struct ti_dt_clk omap54xx_clks[] = { DT_CLK("usbhs_omap", "usbtll_fck", "dummy_ck"), DT_CLK("omap_wdt", "ick", "dummy_ck"), DT_CLK(NULL, "timer_32k_ck", "sys_32k_ck"), + DT_CLK(NULL, "sys_clkin_ck", "sys_clkin"), DT_CLK("4ae18000.timer", "timer_sys_ck", "sys_clkin"), DT_CLK("48032000.timer", "timer_sys_ck", "sys_clkin"), DT_CLK("48034000.timer", "timer_sys_ck", "sys_clkin"), diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c index 6b5a309d9939..bfa17d33ef3b 100644 --- a/drivers/clk/ti/clk-7xx.c +++ b/drivers/clk/ti/clk-7xx.c @@ -289,6 +289,7 @@ static struct ti_dt_clk dra7xx_clks[] = { DT_CLK("usbhs_omap", "usbtll_fck", "dummy_ck"), DT_CLK("omap_wdt", "ick", "dummy_ck"), DT_CLK(NULL, "timer_32k_ck", "sys_32k_ck"), + DT_CLK(NULL, "sys_clkin_ck", "timer_sys_clk_div"), DT_CLK("4ae18000.timer", "timer_sys_ck", "timer_sys_clk_div"), DT_CLK("48032000.timer", "timer_sys_ck", "timer_sys_clk_div"), DT_CLK("48034000.timer", "timer_sys_ck", "timer_sys_clk_div"), diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index 2e14dfb588f4..c77333230bdf 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -265,6 +265,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) /* Get configuration for the ATL instances */ snprintf(prop, sizeof(prop), "atl%u", i); + of_node_get(node); cfg_node = of_find_node_by_name(node, prop); if (cfg_node) { ret = of_property_read_u32(cfg_node, "bws", @@ -278,6 +279,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) atl_write(cinfo, DRA7_ATL_AWSMUX_REG(i), cdesc->aws); } + of_node_put(cfg_node); } cdesc->probed = true; diff --git a/drivers/clk/ti/clkt_dflt.c b/drivers/clk/ti/clkt_dflt.c index 1ddc288fce4e..c6ae563801d7 100644 --- a/drivers/clk/ti/clkt_dflt.c +++ b/drivers/clk/ti/clkt_dflt.c @@ -222,7 +222,7 @@ int omap2_dflt_clk_enable(struct clk_hw *hw) } } - if (unlikely(IS_ERR(clk->enable_reg))) { + if (IS_ERR(clk->enable_reg)) { pr_err("%s: %s missing enable_reg\n", __func__, clk_hw_get_name(hw)); ret = -EINVAL; diff --git a/drivers/clk/ti/clkt_dpll.c b/drivers/clk/ti/clkt_dpll.c index 032c658a5f5e..b919fdfe8256 100644 --- a/drivers/clk/ti/clkt_dpll.c +++ b/drivers/clk/ti/clkt_dpll.c @@ -301,6 +301,9 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, dd = clk->dpll_data; + if (dd->max_rate && target_rate > dd->max_rate) + target_rate = dd->max_rate; + ref_rate = clk_hw_get_rate(dd->clk_ref); clk_name = clk_hw_get_name(hw); pr_debug("clock: %s: starting DPLL round_rate, target rate %lu\n", diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 3bc9959f71c3..9fc8754a6e61 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -655,6 +655,7 @@ static void __init of_ti_am3_no_gate_dpll_setup(struct device_node *node) .max_multiplier = 2047, .max_divider = 128, .min_divider = 1, + .max_rate = 1000000000, .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; @@ -674,6 +675,7 @@ static void __init of_ti_am3_jtype_dpll_setup(struct device_node *node) .max_divider = 256, .min_divider = 2, .flags = DPLL_J_TYPE, + .max_rate = 2000000000, .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; @@ -692,6 +694,7 @@ static void __init of_ti_am3_no_gate_jtype_dpll_setup(struct device_node *node) .max_multiplier = 2047, .max_divider = 128, .min_divider = 1, + .max_rate = 2000000000, .flags = DPLL_J_TYPE, .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; @@ -712,6 +715,7 @@ static void __init of_ti_am3_dpll_setup(struct device_node *node) .max_multiplier = 2047, .max_divider = 128, .min_divider = 1, + .max_rate = 1000000000, .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; @@ -729,6 +733,7 @@ static void __init of_ti_am3_core_dpll_setup(struct device_node *node) .max_multiplier = 2047, .max_divider = 128, .min_divider = 1, + .max_rate = 1000000000, .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; diff --git a/drivers/clk/zte/clk-zx296702.c b/drivers/clk/zte/clk-zx296702.c index ebd20d852e73..76e967c19775 100644 --- a/drivers/clk/zte/clk-zx296702.c +++ b/drivers/clk/zte/clk-zx296702.c @@ -234,8 +234,7 @@ static void __init zx296702_top_clocks_init(struct device_node *np) WARN_ON(!topcrm_base); clk[ZX296702_OSC] = - clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT, - 30000000); + clk_register_fixed_rate(NULL, "osc", NULL, 0, 30000000); clk[ZX296702_PLL_A9] = clk_register_zx_pll("pll_a9", "osc", 0, topcrm_base + 0x01c, pll_a9_config, |