summaryrefslogtreecommitdiff
path: root/drivers/clk/meson
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-10 13:38:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-10 13:38:18 -0700
commitb5a53b61a2890ec08f404f524c1c42aa86f09be4 (patch)
treec4e0c40b3408e4f71a69e5f6ab46b802867cab05 /drivers/clk/meson
parentc70422f760c120480fee4de6c38804c72aa26bc1 (diff)
parent0119dc6132d2110df8f3545bd0ffe29aa0752d6b (diff)
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd: "Sort of on the quieter side this time, which is probably due more to me not catching up as quickly on patch review than anything else. Overall it seems normal though, a few small changes to the core, mostly small non-critical fixes here and there as well as driver updates for new and existing hardware support. The biggest things are the TI clk driver rework to lay the groundwork for clkctrl support in the next merge window and the AmLogic audio/graphics clk support. Core: - clk_possible_parents debugfs file so we know which parents a clk could possibly have - Fix to make clk rate change notifiers stop on the first failure instead of continuing New Drivers: - Mediatek MT6797 SoCs - hi655x PMIC clks - AmLogic Meson SoC i2s and spdif audio clks and Mali graphics clks - Allwinner H5 SoCs and PRCM hardware Updates: - Nvidia Tegra T210 cleanups and non-critical fixes - TI OMAP cleanups in preparation for clkctrl support - trivial fixes like kcalloc(), devm_* conversions, and seq_puts() - ZTE zx296718 SoC VGA clks - Rockchip clk-ids, fixups, and rename of rk1108 to rv1108 - IDT VersaClock 5P49V5935 support - Renesas R-Car H3 and M3-W IMR clks and ES2.0 rev of R-Car H3 support" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (151 commits) clk: x86: pmc-atom: Checking for IS_ERR() instead of NULL clk: ti: divider: try to fix ti_clk_register_divider clk: mvebu: Use kcalloc() in two functions clk: mvebu: Use kcalloc() in of_cpu_clk_setup() clk: nomadik: Delete error messages for a failed memory allocation in two functions clk: nomadik: Use seq_puts() in nomadik_src_clk_show() clk: Improve a size determination in two functions clk: Replace four seq_printf() calls by seq_putc() clk: si5351: Delete an error message for a failed memory allocation in si5351_i2c_probe() clk: si5351: Use devm_kcalloc() in si5351_i2c_probe() clk: at91: Use kcalloc() in of_at91_clk_pll_get_characteristics() reset: mediatek: Add MT2701 ethsys reset controller include file clk: mediatek: add mt2701 ethernet reset clk: hi6220: Add the hi655x's pmic clock clk: ti: fix building without legacy omap3 clk: ti: fix linker error with !SOC_OMAP4 clk: hi3620: Fix a typo in one variable name clk: hi3620: Delete error messages for a failed memory allocation in two functions clk: hi3620: Use kcalloc() in hi3620_mmc_clk_init() clk: hisilicon: Delete error messages for failed memory allocations in hisi_clk_init() ...
Diffstat (limited to 'drivers/clk/meson')
-rw-r--r--drivers/clk/meson/Makefile2
-rw-r--r--drivers/clk/meson/clk-audio-divider.c144
-rw-r--r--drivers/clk/meson/clk-mpll.c154
-rw-r--r--drivers/clk/meson/clk-pll.c53
-rw-r--r--drivers/clk/meson/clkc.h39
-rw-r--r--drivers/clk/meson/gxbb.c649
-rw-r--r--drivers/clk/meson/gxbb.h11
-rw-r--r--drivers/clk/meson/meson8b.c127
-rw-r--r--drivers/clk/meson/meson8b.h20
9 files changed, 1150 insertions, 49 deletions
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 349583405b7c..83b6d9d65aa1 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -2,6 +2,6 @@
# Makefile for Meson specific clk
#
-obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
diff --git a/drivers/clk/meson/clk-audio-divider.c b/drivers/clk/meson/clk-audio-divider.c
new file mode 100644
index 000000000000..6c07db06642d
--- /dev/null
+++ b/drivers/clk/meson/clk-audio-divider.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017 AmLogic, Inc.
+ * Author: Jerome Brunet <jbrunet@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/>.
+ */
+
+/*
+ * i2s master clock divider: The algorithm of the generic clk-divider used with
+ * a very precise clock parent such as the mpll tends to select a low divider
+ * factor. This gives poor results with this particular divider, especially with
+ * high frequencies (> 100 MHz)
+ *
+ * This driver try to select the maximum possible divider with the rate the
+ * upstream clock can provide.
+ */
+
+#include <linux/clk-provider.h>
+#include "clkc.h"
+
+#define to_meson_clk_audio_divider(_hw) container_of(_hw, \
+ struct meson_clk_audio_divider, hw)
+
+static int _div_round(unsigned long parent_rate, unsigned long rate,
+ unsigned long flags)
+{
+ if (flags & CLK_DIVIDER_ROUND_CLOSEST)
+ return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, rate);
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
+}
+
+static int _get_val(unsigned long parent_rate, unsigned long rate)
+{
+ return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
+}
+
+static int _valid_divider(struct clk_hw *hw, int divider)
+{
+ struct meson_clk_audio_divider *adiv =
+ to_meson_clk_audio_divider(hw);
+ int max_divider;
+ u8 width;
+
+ width = adiv->div.width;
+ max_divider = 1 << width;
+
+ return clamp(divider, 1, max_divider);
+}
+
+static unsigned long audio_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct meson_clk_audio_divider *adiv =
+ to_meson_clk_audio_divider(hw);
+ struct parm *p;
+ unsigned long reg, divider;
+
+ p = &adiv->div;
+ reg = readl(adiv->base + p->reg_off);
+ divider = PARM_GET(p->width, p->shift, reg) + 1;
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, divider);
+}
+
+static long audio_divider_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct meson_clk_audio_divider *adiv =
+ to_meson_clk_audio_divider(hw);
+ unsigned long max_prate;
+ int divider;
+
+ if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+ divider = _div_round(*parent_rate, rate, adiv->flags);
+ divider = _valid_divider(hw, divider);
+ return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
+ }
+
+ /* Get the maximum parent rate */
+ max_prate = clk_hw_round_rate(clk_hw_get_parent(hw), ULONG_MAX);
+
+ /* Get the corresponding rounded down divider */
+ divider = max_prate / rate;
+ divider = _valid_divider(hw, divider);
+
+ /* Get actual rate of the parent */
+ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
+ divider * rate);
+
+ return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
+}
+
+static int audio_divider_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct meson_clk_audio_divider *adiv =
+ to_meson_clk_audio_divider(hw);
+ struct parm *p;
+ unsigned long reg, flags = 0;
+ int val;
+
+ val = _get_val(parent_rate, rate);
+
+ if (adiv->lock)
+ spin_lock_irqsave(adiv->lock, flags);
+ else
+ __acquire(adiv->lock);
+
+ p = &adiv->div;
+ reg = readl(adiv->base + p->reg_off);
+ reg = PARM_SET(p->width, p->shift, reg, val);
+ writel(reg, adiv->base + p->reg_off);
+
+ if (adiv->lock)
+ spin_unlock_irqrestore(adiv->lock, flags);
+ else
+ __release(adiv->lock);
+
+ return 0;
+}
+
+const struct clk_ops meson_clk_audio_divider_ro_ops = {
+ .recalc_rate = audio_divider_recalc_rate,
+ .round_rate = audio_divider_round_rate,
+};
+
+const struct clk_ops meson_clk_audio_divider_ops = {
+ .recalc_rate = audio_divider_recalc_rate,
+ .round_rate = audio_divider_round_rate,
+ .set_rate = audio_divider_set_rate,
+};
diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c
index 03af79005ddb..39eab69fe51a 100644
--- a/drivers/clk/meson/clk-mpll.c
+++ b/drivers/clk/meson/clk-mpll.c
@@ -64,17 +64,51 @@
#include <linux/clk-provider.h>
#include "clkc.h"
-#define SDM_MAX 16384
+#define SDM_DEN 16384
+#define N2_MIN 4
+#define N2_MAX 511
#define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)
+static long rate_from_params(unsigned long parent_rate,
+ unsigned long sdm,
+ unsigned long n2)
+{
+ unsigned long divisor = (SDM_DEN * n2) + sdm;
+
+ if (n2 < N2_MIN)
+ return -EINVAL;
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor);
+}
+
+static void params_from_rate(unsigned long requested_rate,
+ unsigned long parent_rate,
+ unsigned long *sdm,
+ unsigned long *n2)
+{
+ uint64_t div = parent_rate;
+ unsigned long rem = do_div(div, requested_rate);
+
+ if (div < N2_MIN) {
+ *n2 = N2_MIN;
+ *sdm = 0;
+ } else if (div > N2_MAX) {
+ *n2 = N2_MAX;
+ *sdm = SDM_DEN - 1;
+ } else {
+ *n2 = div;
+ *sdm = DIV_ROUND_UP(rem * SDM_DEN, requested_rate);
+ }
+}
+
static unsigned long mpll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
struct parm *p;
- unsigned long rate = 0;
unsigned long reg, sdm, n2;
+ long rate;
p = &mpll->sdm;
reg = readl(mpll->base + p->reg_off);
@@ -84,11 +118,123 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
reg = readl(mpll->base + p->reg_off);
n2 = PARM_GET(p->width, p->shift, reg);
- rate = (parent_rate * SDM_MAX) / ((SDM_MAX * n2) + sdm);
+ rate = rate_from_params(parent_rate, sdm, n2);
+ if (rate < 0)
+ return 0;
return rate;
}
+static long mpll_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long sdm, n2;
+
+ params_from_rate(rate, *parent_rate, &sdm, &n2);
+ return rate_from_params(*parent_rate, sdm, n2);
+}
+
+static int mpll_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
+ struct parm *p;
+ unsigned long reg, sdm, n2;
+ unsigned long flags = 0;
+
+ params_from_rate(rate, parent_rate, &sdm, &n2);
+
+ if (mpll->lock)
+ spin_lock_irqsave(mpll->lock, flags);
+ else
+ __acquire(mpll->lock);
+
+ p = &mpll->sdm;
+ reg = readl(mpll->base + p->reg_off);
+ reg = PARM_SET(p->width, p->shift, reg, sdm);
+ writel(reg, mpll->base + p->reg_off);
+
+ p = &mpll->sdm_en;
+ reg = readl(mpll->base + p->reg_off);
+ reg = PARM_SET(p->width, p->shift, reg, 1);
+ writel(reg, mpll->base + p->reg_off);
+
+ p = &mpll->n2;
+ reg = readl(mpll->base + p->reg_off);
+ reg = PARM_SET(p->width, p->shift, reg, n2);
+ writel(reg, mpll->base + p->reg_off);
+
+ if (mpll->lock)
+ spin_unlock_irqrestore(mpll->lock, flags);
+ else
+ __release(mpll->lock);
+
+ return 0;
+}
+
+static void mpll_enable_core(struct clk_hw *hw, int enable)
+{
+ struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
+ struct parm *p;
+ unsigned long reg;
+ unsigned long flags = 0;
+
+ if (mpll->lock)
+ spin_lock_irqsave(mpll->lock, flags);
+ else
+ __acquire(mpll->lock);
+
+ p = &mpll->en;
+ reg = readl(mpll->base + p->reg_off);
+ reg = PARM_SET(p->width, p->shift, reg, enable ? 1 : 0);
+ writel(reg, mpll->base + p->reg_off);
+
+ if (mpll->lock)
+ spin_unlock_irqrestore(mpll->lock, flags);
+ else
+ __release(mpll->lock);
+}
+
+
+static int mpll_enable(struct clk_hw *hw)
+{
+ mpll_enable_core(hw, 1);
+
+ return 0;
+}
+
+static void mpll_disable(struct clk_hw *hw)
+{
+ mpll_enable_core(hw, 0);
+}
+
+static int mpll_is_enabled(struct clk_hw *hw)
+{
+ struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
+ struct parm *p;
+ unsigned long reg;
+ int en;
+
+ p = &mpll->en;
+ reg = readl(mpll->base + p->reg_off);
+ en = PARM_GET(p->width, p->shift, reg);
+
+ return en;
+}
+
const struct clk_ops meson_clk_mpll_ro_ops = {
- .recalc_rate = mpll_recalc_rate,
+ .recalc_rate = mpll_recalc_rate,
+ .round_rate = mpll_round_rate,
+ .is_enabled = mpll_is_enabled,
+};
+
+const struct clk_ops meson_clk_mpll_ops = {
+ .recalc_rate = mpll_recalc_rate,
+ .round_rate = mpll_round_rate,
+ .set_rate = mpll_set_rate,
+ .enable = mpll_enable,
+ .disable = mpll_disable,
+ .is_enabled = mpll_is_enabled,
};
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 4adc1e89212c..01341553f50b 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -116,6 +116,30 @@ static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_
return NULL;
}
+/* Specific wait loop for GXL/GXM GP0 PLL */
+static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
+ struct parm *p_n)
+{
+ int delay = 100;
+ u32 reg;
+
+ while (delay > 0) {
+ reg = readl(pll->base + p_n->reg_off);
+ writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
+ udelay(10);
+ writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);
+
+ /* This delay comes from AMLogic tree clk-gp0-gxl driver */
+ mdelay(1);
+
+ reg = readl(pll->base + p_n->reg_off);
+ if (reg & MESON_PLL_LOCK)
+ return 0;
+ delay--;
+ }
+ return -ETIMEDOUT;
+}
+
static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
struct parm *p_n)
{
@@ -132,6 +156,15 @@ static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
return -ETIMEDOUT;
}
+static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
+{
+ int i;
+
+ for (i = 0 ; i < pll->params.params_count ; ++i)
+ writel(pll->params.params_table[i].value,
+ pll->base + pll->params.params_table[i].reg_off);
+}
+
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@@ -151,10 +184,16 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
if (!rate_set)
return -EINVAL;
+ /* Initialize the PLL in a clean state if specified */
+ if (pll->params.params_count)
+ meson_clk_pll_init_params(pll);
+
/* PLL reset */
p = &pll->n;
reg = readl(pll->base + p->reg_off);
- writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
+ /* If no_init_reset is provided, avoid resetting at this point */
+ if (!pll->params.no_init_reset)
+ writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
writel(reg, pll->base + p->reg_off);
@@ -184,7 +223,17 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
}
p = &pll->n;
- ret = meson_clk_pll_wait_lock(pll, p);
+ /* If clear_reset_for_lock is provided, remove the reset bit here */
+ if (pll->params.clear_reset_for_lock) {
+ reg = readl(pll->base + p->reg_off);
+ writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
+ }
+
+ /* If reset_lock_loop, use a special loop including resetting */
+ if (pll->params.reset_lock_loop)
+ ret = meson_clk_pll_wait_lock_reset(pll, p);
+ else
+ ret = meson_clk_pll_wait_lock(pll, p);
if (ret) {
pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
__func__, old_rate);
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 9bb70e7a7d6a..d6feafe8bd6c 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -25,7 +25,7 @@
#define PARM_GET(width, shift, reg) \
(((reg) & SETPMASK(width, shift)) >> (shift))
#define PARM_SET(width, shift, reg, val) \
- (((reg) & CLRPMASK(width, shift)) | (val << (shift)))
+ (((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))
#define MESON_PARM_APPLICABLE(p) (!!((p)->width))
@@ -62,6 +62,28 @@ struct pll_rate_table {
.frac = (_frac), \
} \
+struct pll_params_table {
+ unsigned int reg_off;
+ unsigned int value;
+};
+
+#define PLL_PARAM(_reg, _val) \
+ { \
+ .reg_off = (_reg), \
+ .value = (_val), \
+ }
+
+struct pll_setup_params {
+ struct pll_params_table *params_table;
+ unsigned int params_count;
+ /* Workaround for GP0, do not reset before configuring */
+ bool no_init_reset;
+ /* Workaround for GP0, unreset right before checking for lock */
+ bool clear_reset_for_lock;
+ /* Workaround for GXL GP0, reset in the lock checking loop */
+ bool reset_lock_loop;
+};
+
struct meson_clk_pll {
struct clk_hw hw;
void __iomem *base;
@@ -70,6 +92,7 @@ struct meson_clk_pll {
struct parm frac;
struct parm od;
struct parm od2;
+ const struct pll_setup_params params;
const struct pll_rate_table *rate_table;
unsigned int rate_count;
spinlock_t *lock;
@@ -92,8 +115,17 @@ struct meson_clk_mpll {
struct clk_hw hw;
void __iomem *base;
struct parm sdm;
+ struct parm sdm_en;
struct parm n2;
- /* FIXME ssen gate control? */
+ struct parm en;
+ spinlock_t *lock;
+};
+
+struct meson_clk_audio_divider {
+ struct clk_hw hw;
+ void __iomem *base;
+ struct parm div;
+ u8 flags;
spinlock_t *lock;
};
@@ -116,5 +148,8 @@ extern const struct clk_ops meson_clk_pll_ro_ops;
extern const struct clk_ops meson_clk_pll_ops;
extern const struct clk_ops meson_clk_cpu_ops;
extern const struct clk_ops meson_clk_mpll_ro_ops;
+extern const struct clk_ops meson_clk_mpll_ops;
+extern const struct clk_ops meson_clk_audio_divider_ro_ops;
+extern const struct clk_ops meson_clk_audio_divider_ops;
#endif /* __CLKC_H */
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 1c1ec137a3cc..ad5f027af1a2 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -20,6 +20,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/init.h>
@@ -120,7 +121,7 @@ static const struct pll_rate_table sys_pll_rate_table[] = {
{ /* sentinel */ },
};
-static const struct pll_rate_table gp0_pll_rate_table[] = {
+static const struct pll_rate_table gxbb_gp0_pll_rate_table[] = {
PLL_RATE(96000000, 32, 1, 3),
PLL_RATE(99000000, 33, 1, 3),
PLL_RATE(102000000, 34, 1, 3),
@@ -248,6 +249,35 @@ static const struct pll_rate_table gp0_pll_rate_table[] = {
{ /* sentinel */ },
};
+static const struct pll_rate_table gxl_gp0_pll_rate_table[] = {
+ PLL_RATE(504000000, 42, 1, 1),
+ PLL_RATE(516000000, 43, 1, 1),
+ PLL_RATE(528000000, 44, 1, 1),
+ PLL_RATE(540000000, 45, 1, 1),
+ PLL_RATE(552000000, 46, 1, 1),
+ PLL_RATE(564000000, 47, 1, 1),
+ PLL_RATE(576000000, 48, 1, 1),
+ PLL_RATE(588000000, 49, 1, 1),
+ PLL_RATE(600000000, 50, 1, 1),
+ PLL_RATE(612000000, 51, 1, 1),
+ PLL_RATE(624000000, 52, 1, 1),
+ PLL_RATE(636000000, 53, 1, 1),
+ PLL_RATE(648000000, 54, 1, 1),
+ PLL_RATE(660000000, 55, 1, 1),
+ PLL_RATE(672000000, 56, 1, 1),
+ PLL_RATE(684000000, 57, 1, 1),
+ PLL_RATE(696000000, 58, 1, 1),
+ PLL_RATE(708000000, 59, 1, 1),
+ PLL_RATE(720000000, 60, 1, 1),
+ PLL_RATE(732000000, 61, 1, 1),
+ PLL_RATE(744000000, 62, 1, 1),
+ PLL_RATE(756000000, 63, 1, 1),
+ PLL_RATE(768000000, 64, 1, 1),
+ PLL_RATE(780000000, 65, 1, 1),
+ PLL_RATE(792000000, 66, 1, 1),
+ { /* sentinel */ },
+};
+
static const struct clk_div_table cpu_div_table[] = {
{ .val = 1, .div = 1 },
{ .val = 2, .div = 2 },
@@ -352,6 +382,13 @@ static struct meson_clk_pll gxbb_sys_pll = {
},
};
+struct pll_params_table gxbb_gp0_params_table[] = {
+ PLL_PARAM(HHI_GP0_PLL_CNTL, 0x6a000228),
+ PLL_PARAM(HHI_GP0_PLL_CNTL2, 0x69c80000),
+ PLL_PARAM(HHI_GP0_PLL_CNTL3, 0x0a5590c4),
+ PLL_PARAM(HHI_GP0_PLL_CNTL4, 0x0000500d),
+};
+
static struct meson_clk_pll gxbb_gp0_pll = {
.m = {
.reg_off = HHI_GP0_PLL_CNTL,
@@ -368,8 +405,57 @@ static struct meson_clk_pll gxbb_gp0_pll = {
.shift = 16,
.width = 2,
},
- .rate_table = gp0_pll_rate_table,
- .rate_count = ARRAY_SIZE(gp0_pll_rate_table),
+ .params = {
+ .params_table = gxbb_gp0_params_table,
+ .params_count = ARRAY_SIZE(gxbb_gp0_params_table),
+ .no_init_reset = true,
+ .clear_reset_for_lock = true,
+ },
+ .rate_table = gxbb_gp0_pll_rate_table,
+ .rate_count = ARRAY_SIZE(gxbb_gp0_pll_rate_table),
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll",
+ .ops = &meson_clk_pll_ops,
+ .parent_names = (const char *[]){ "xtal" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+struct pll_params_table gxl_gp0_params_table[] = {
+ PLL_PARAM(HHI_GP0_PLL_CNTL, 0x40010250),
+ PLL_PARAM(HHI_GP0_PLL_CNTL1, 0xc084a000),
+ PLL_PARAM(HHI_GP0_PLL_CNTL2, 0xb75020be),
+ PLL_PARAM(HHI_GP0_PLL_CNTL3, 0x0a59a288),
+ PLL_PARAM(HHI_GP0_PLL_CNTL4, 0xc000004d),
+ PLL_PARAM(HHI_GP0_PLL_CNTL5, 0x00078000),
+};
+
+static struct meson_clk_pll gxl_gp0_pll = {
+ .m = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .params = {
+ .params_table = gxl_gp0_params_table,
+ .params_count = ARRAY_SIZE(gxl_gp0_params_table),
+ .no_init_reset = true,
+ .reset_lock_loop = true,
+ },
+ .rate_table = gxl_gp0_pll_rate_table,
+ .rate_count = ARRAY_SIZE(gxl_gp0_pll_rate_table),
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "gp0_pll",
@@ -441,15 +527,25 @@ static struct meson_clk_mpll gxbb_mpll0 = {
.shift = 0,
.width = 14,
},
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 15,
+ .width = 1,
+ },
.n2 = {
.reg_off = HHI_MPLL_CNTL7,
.shift = 16,
.width = 9,
},
+ .en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 14,
+ .width = 1,
+ },
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "mpll0",
- .ops = &meson_clk_mpll_ro_ops,
+ .ops = &meson_clk_mpll_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
@@ -461,15 +557,25 @@ static struct meson_clk_mpll gxbb_mpll1 = {
.shift = 0,
.width = 14,
},
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 15,
+ .width = 1,
+ },
.n2 = {
.reg_off = HHI_MPLL_CNTL8,
.shift = 16,
.width = 9,
},
+ .en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 14,
+ .width = 1,
+ },
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "mpll1",
- .ops = &meson_clk_mpll_ro_ops,
+ .ops = &meson_clk_mpll_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
@@ -481,15 +587,25 @@ static struct meson_clk_mpll gxbb_mpll2 = {
.shift = 0,
.width = 14,
},
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 15,
+ .width = 1,
+ },
.n2 = {
.reg_off = HHI_MPLL_CNTL9,
.shift = 16,
.width = 9,
},
+ .en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 14,
+ .width = 1,
+ },
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
.name = "mpll2",
- .ops = &meson_clk_mpll_ro_ops,
+ .ops = &meson_clk_mpll_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
@@ -604,6 +720,237 @@ static struct clk_gate gxbb_sar_adc_clk = {
},
};
+/*
+ * The MALI IP is clocked by two identical clocks (mali_0 and mali_1)
+ * muxed by a glitch-free switch.
+ */
+
+static u32 mux_table_mali_0_1[] = {0, 1, 2, 3, 4, 5, 6, 7};
+static const char *gxbb_mali_0_1_parent_names[] = {
+ "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7",
+ "fclk_div4", "fclk_div3", "fclk_div5"
+};
+
+static struct clk_mux gxbb_mali_0_sel = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ .table = mux_table_mali_0_1,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_sel",
+ .ops = &clk_mux_ops,
+ /*
+ * bits 10:9 selects from 8 possible parents:
+ * xtal, gp0_pll, mpll2, mpll1, fclk_div7,
+ * fclk_div4, fclk_div3, fclk_div5
+ */
+ .parent_names = gxbb_mali_0_1_parent_names,
+ .num_parents = 8,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_divider gxbb_mali_0_div = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_div",
+ .ops = &clk_divider_ops,
+ .parent_names = (const char *[]){ "mali_0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_gate gxbb_mali_0 = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .bit_idx = 8,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0",
+ .ops = &clk_gate_ops,
+ .parent_names = (const char *[]){ "mali_0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_mux gxbb_mali_1_sel = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ .table = mux_table_mali_0_1,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_sel",
+ .ops = &clk_mux_ops,
+ /*
+ * bits 10:9 selects from 8 possible parents:
+ * xtal, gp0_pll, mpll2, mpll1, fclk_div7,
+ * fclk_div4, fclk_div3, fclk_div5
+ */
+ .parent_names = gxbb_mali_0_1_parent_names,
+ .num_parents = 8,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_divider gxbb_mali_1_div = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_div",
+ .ops = &clk_divider_ops,
+ .parent_names = (const char *[]){ "mali_1_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_gate gxbb_mali_1 = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .bit_idx = 24,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1",
+ .ops = &clk_gate_ops,
+ .parent_names = (const char *[]){ "mali_1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static u32 mux_table_mali[] = {0, 1};
+static const char *gxbb_mali_parent_names[] = {
+ "mali_0", "mali_1"
+};
+
+static struct clk_mux gxbb_mali = {
+ .reg = (void *)HHI_MALI_CLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ .table = mux_table_mali,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mali",
+ .ops = &clk_mux_ops,
+ .parent_names = gxbb_mali_parent_names,
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_mux gxbb_cts_amclk_sel = {
+ .reg = (void *) HHI_AUD_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ /* Default parent unknown (register reset value: 0) */
+ .table = (u32[]){ 1, 2, 3 },
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_amclk_sel",
+ .ops = &clk_mux_ops,
+ .parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
+ .num_parents = 3,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct meson_clk_audio_divider gxbb_cts_amclk_div = {
+ .div = {
+ .reg_off = HHI_AUD_CLK_CNTL,
+ .shift = 0,
+ .width = 8,
+ },
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_amclk_div",
+ .ops = &meson_clk_audio_divider_ops,
+ .parent_names = (const char *[]){ "cts_amclk_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST,
+ },
+};
+
+static struct clk_gate gxbb_cts_amclk = {
+ .reg = (void *) HHI_AUD_CLK_CNTL,
+ .bit_idx = 8,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_amclk",
+ .ops = &clk_gate_ops,
+ .parent_names = (const char *[]){ "cts_amclk_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_mux gxbb_cts_mclk_i958_sel = {
+ .reg = (void *)HHI_AUD_CLK_CNTL2,
+ .mask = 0x3,
+ .shift = 25,
+ /* Default parent unknown (register reset value: 0) */
+ .table = (u32[]){ 1, 2, 3 },
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_mclk_i958_sel",
+ .ops = &clk_mux_ops,
+ .parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
+ .num_parents = 3,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_divider gxbb_cts_mclk_i958_div = {
+ .reg = (void *)HHI_AUD_CLK_CNTL2,
+ .shift = 16,
+ .width = 8,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_mclk_i958_div",
+ .ops = &clk_divider_ops,
+ .parent_names = (const char *[]){ "cts_mclk_i958_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST,
+ },
+};
+
+static struct clk_gate gxbb_cts_mclk_i958 = {
+ .reg = (void *)HHI_AUD_CLK_CNTL2,
+ .bit_idx = 24,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_mclk_i958",
+ .ops = &clk_gate_ops,
+ .parent_names = (const char *[]){ "cts_mclk_i958_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_mux gxbb_cts_i958 = {
+ .reg = (void *)HHI_AUD_CLK_CNTL2,
+ .mask = 0x1,
+ .shift = 27,
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_i958",
+ .ops = &clk_mux_ops,
+ .parent_names = (const char *[]){ "cts_amclk", "cts_mclk_i958" },
+ .num_parents = 2,
+ /*
+ *The parent is specific to origin of the audio data. Let the
+ * consumer choose the appropriate parent
+ */
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
/* Everything Else (EE) domain gates */
static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0);
static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1);
@@ -797,6 +1144,140 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
[CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw,
[CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw,
[CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw,
+ [CLKID_MALI_0_SEL] = &gxbb_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &gxbb_mali_0_div.hw,
+ [CLKID_MALI_0] = &gxbb_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &gxbb_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &gxbb_mali_1_div.hw,
+ [CLKID_MALI_1] = &gxbb_mali_1.hw,
+ [CLKID_MALI] = &gxbb_mali.hw,
+ [CLKID_CTS_AMCLK] = &gxbb_cts_amclk.hw,
+ [CLKID_CTS_AMCLK_SEL] = &gxbb_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &gxbb_cts_amclk_div.hw,
+ [CLKID_CTS_MCLK_I958] = &gxbb_cts_mclk_i958.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &gxbb_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &gxbb_cts_mclk_i958_div.hw,
+ [CLKID_CTS_I958] = &gxbb_cts_i958.hw,
+ },
+ .num = NR_CLKS,
+};
+
+static struct clk_hw_onecell_data gxl_hw_onecell_data = {
+ .hws = {
+ [CLKID_SYS_PLL] = &gxbb_sys_pll.hw,
+ [CLKID_CPUCLK] = &gxbb_cpu_clk.hw,
+ [CLKID_HDMI_PLL] = &gxbb_hdmi_pll.hw,
+ [CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &gxbb_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &gxbb_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &gxbb_fclk_div7.hw,
+ [CLKID_GP0_PLL] = &gxl_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &gxbb_mpeg_clk_sel.hw,
+ [CLKID_MPEG_DIV] = &gxbb_mpeg_clk_div.hw,
+ [CLKID_CLK81] = &gxbb_clk81.hw,
+ [CLKID_MPLL0] = &gxbb_mpll0.hw,
+ [CLKID_MPLL1] = &gxbb_mpll1.hw,
+ [CLKID_MPLL2] = &gxbb_mpll2.hw,
+ [CLKID_DDR] = &gxbb_ddr.hw,
+ [CLKID_DOS] = &gxbb_dos.hw,
+ [CLKID_ISA] = &gxbb_isa.hw,
+ [CLKID_PL301] = &gxbb_pl301.hw,
+ [CLKID_PERIPHS] = &gxbb_periphs.hw,
+ [CLKID_SPICC] = &gxbb_spicc.hw,
+ [CLKID_I2C] = &gxbb_i2c.hw,
+ [CLKID_SAR_ADC] = &gxbb_sar_adc.hw,
+ [CLKID_SMART_CARD] = &gxbb_smart_card.hw,
+ [CLKID_RNG0] = &gxbb_rng0.hw,
+ [CLKID_UART0] = &gxbb_uart0.hw,
+ [CLKID_SDHC] = &gxbb_sdhc.hw,
+ [CLKID_STREAM] = &gxbb_stream.hw,
+ [CLKID_ASYNC_FIFO] = &gxbb_async_fifo.hw,
+ [CLKID_SDIO] = &gxbb_sdio.hw,
+ [CLKID_ABUF] = &gxbb_abuf.hw,
+ [CLKID_HIU_IFACE] = &gxbb_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &gxbb_assist_misc.hw,
+ [CLKID_SPI] = &gxbb_spi.hw,
+ [CLKID_I2S_SPDIF] = &gxbb_i2s_spdif.hw,
+ [CLKID_ETH] = &gxbb_eth.hw,
+ [CLKID_DEMUX] = &gxbb_demux.hw,
+ [CLKID_AIU_GLUE] = &gxbb_aiu_glue.hw,
+ [CLKID_IEC958] = &gxbb_iec958.hw,
+ [CLKID_I2S_OUT] = &gxbb_i2s_out.hw,
+ [CLKID_AMCLK] = &gxbb_amclk.hw,
+ [CLKID_AIFIFO2] = &gxbb_aififo2.hw,
+ [CLKID_MIXER] = &gxbb_mixer.hw,
+ [CLKID_MIXER_IFACE] = &gxbb_mixer_iface.hw,
+ [CLKID_ADC] = &gxbb_adc.hw,
+ [CLKID_BLKMV] = &gxbb_blkmv.hw,
+ [CLKID_AIU] = &gxbb_aiu.hw,
+ [CLKID_UART1] = &gxbb_uart1.hw,
+ [CLKID_G2D] = &gxbb_g2d.hw,
+ [CLKID_USB0] = &gxbb_usb0.hw,
+ [CLKID_USB1] = &gxbb_usb1.hw,
+ [CLKID_RESET] = &gxbb_reset.hw,
+ [CLKID_NAND] = &gxbb_nand.hw,
+ [CLKID_DOS_PARSER] = &gxbb_dos_parser.hw,
+ [CLKID_USB] = &gxbb_usb.hw,
+ [CLKID_VDIN1] = &gxbb_vdin1.hw,
+ [CLKID_AHB_ARB0] = &gxbb_ahb_arb0.hw,
+ [CLKID_EFUSE] = &gxbb_efuse.hw,
+ [CLKID_BOOT_ROM] = &gxbb_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &gxbb_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &gxbb_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &gxbb_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &gxbb_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &gxbb_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &gxbb_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &gxbb_mmc_pclk.hw,
+ [CLKID_DVIN] = &gxbb_dvin.hw,
+ [CLKID_UART2] = &gxbb_uart2.hw,
+ [CLKID_SANA] = &gxbb_sana.hw,
+ [CLKID_VPU_INTR] = &gxbb_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &gxbb_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A53] = &gxbb_clk81_a53.hw,
+ [CLKID_VCLK2_VENCI0] = &gxbb_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &gxbb_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &gxbb_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &gxbb_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT0] = &gxbb_gclk_venci_int0.hw,
+ [CLKID_GCLK_VENCI_INT] = &gxbb_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &gxbb_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &gxbb_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &gxbb_iec958_gate.hw,
+ [CLKID_ENC480P] = &gxbb_enc480p.hw,
+ [CLKID_RNG1] = &gxbb_rng1.hw,
+ [CLKID_GCLK_VENCI_INT1] = &gxbb_gclk_venci_int1.hw,
+ [CLKID_VCLK2_VENCLMCC] = &gxbb_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &gxbb_vclk2_vencl.hw,
+ [CLKID_VCLK_OTHER] = &gxbb_vclk_other.hw,
+ [CLKID_EDP] = &gxbb_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &gxbb_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &gxbb_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &gxbb_ao_iface.hw,
+ [CLKID_AO_I2C] = &gxbb_ao_i2c.hw,
+ [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw,
+ [CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw,
+ [CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw,
+ [CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw,
+ [CLKID_MALI_0_SEL] = &gxbb_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &gxbb_mali_0_div.hw,
+ [CLKID_MALI_0] = &gxbb_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &gxbb_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &gxbb_mali_1_div.hw,
+ [CLKID_MALI_1] = &gxbb_mali_1.hw,
+ [CLKID_MALI] = &gxbb_mali.hw,
+ [CLKID_CTS_AMCLK] = &gxbb_cts_amclk.hw,
+ [CLKID_CTS_AMCLK_SEL] = &gxbb_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &gxbb_cts_amclk_div.hw,
+ [CLKID_CTS_MCLK_I958] = &gxbb_cts_mclk_i958.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &gxbb_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &gxbb_cts_mclk_i958_div.hw,
+ [CLKID_CTS_I958] = &gxbb_cts_i958.hw,
},
.num = NR_CLKS,
};
@@ -810,13 +1291,20 @@ static struct meson_clk_pll *const gxbb_clk_plls[] = {
&gxbb_gp0_pll,
};
+static struct meson_clk_pll *const gxl_clk_plls[] = {
+ &gxbb_fixed_pll,
+ &gxbb_hdmi_pll,
+ &gxbb_sys_pll,
+ &gxl_gp0_pll,
+};
+
static struct meson_clk_mpll *const gxbb_clk_mplls[] = {
&gxbb_mpll0,
&gxbb_mpll1,
&gxbb_mpll2,
};
-static struct clk_gate *gxbb_clk_gates[] = {
+static struct clk_gate *const gxbb_clk_gates[] = {
&gxbb_clk81,
&gxbb_ddr,
&gxbb_dos,
@@ -900,16 +1388,105 @@ static struct clk_gate *gxbb_clk_gates[] = {
&gxbb_emmc_b,
&gxbb_emmc_c,
&gxbb_sar_adc_clk,
+ &gxbb_mali_0,
+ &gxbb_mali_1,
+ &gxbb_cts_amclk,
+ &gxbb_cts_mclk_i958,
+};
+
+static struct clk_mux *const gxbb_clk_muxes[] = {
+ &gxbb_mpeg_clk_sel,
+ &gxbb_sar_adc_clk_sel,
+ &gxbb_mali_0_sel,
+ &gxbb_mali_1_sel,
+ &gxbb_mali,
+ &gxbb_cts_amclk_sel,
+ &gxbb_cts_mclk_i958_sel,
+ &gxbb_cts_i958,
+};
+
+static struct clk_divider *const gxbb_clk_dividers[] = {
+ &gxbb_mpeg_clk_div,
+ &gxbb_sar_adc_clk_div,
+ &gxbb_mali_0_div,
+ &gxbb_mali_1_div,
+ &gxbb_cts_mclk_i958_div,
+};
+
+static struct meson_clk_audio_divider *const gxbb_audio_dividers[] = {
+ &gxbb_cts_amclk_div,
+};
+
+struct clkc_data {
+ struct clk_gate *const *clk_gates;
+ unsigned int clk_gates_count;
+ struct meson_clk_mpll *const *clk_mplls;
+ unsigned int clk_mplls_count;
+ struct meson_clk_pll *const *clk_plls;
+ unsigned int clk_plls_count;
+ struct clk_mux *const *clk_muxes;
+ unsigned int clk_muxes_count;
+ struct clk_divider *const *clk_dividers;
+ unsigned int clk_dividers_count;
+ struct meson_clk_audio_divider *const *clk_audio_dividers;
+ unsigned int clk_audio_dividers_count;
+ struct meson_clk_cpu *cpu_clk;
+ struct clk_hw_onecell_data *hw_onecell_data;
+};
+
+static const struct clkc_data gxbb_clkc_data = {
+ .clk_gates = gxbb_clk_gates,
+ .clk_gates_count = ARRAY_SIZE(gxbb_clk_gates),
+ .clk_mplls = gxbb_clk_mplls,
+ .clk_mplls_count = ARRAY_SIZE(gxbb_clk_mplls),
+ .clk_plls = gxbb_clk_plls,
+ .clk_plls_count = ARRAY_SIZE(gxbb_clk_plls),
+ .clk_muxes = gxbb_clk_muxes,
+ .clk_muxes_count = ARRAY_SIZE(gxbb_clk_muxes),
+ .clk_dividers = gxbb_clk_dividers,
+ .clk_dividers_count = ARRAY_SIZE(gxbb_clk_dividers),
+ .clk_audio_dividers = gxbb_audio_dividers,
+ .clk_audio_dividers_count = ARRAY_SIZE(gxbb_audio_dividers),
+ .cpu_clk = &gxbb_cpu_clk,
+ .hw_onecell_data = &gxbb_hw_onecell_data,
+};
+
+static const struct clkc_data gxl_clkc_data = {
+ .clk_gates = gxbb_clk_gates,
+ .clk_gates_count = ARRAY_SIZE(gxbb_clk_gates),
+ .clk_mplls = gxbb_clk_mplls,
+ .clk_mplls_count = ARRAY_SIZE(gxbb_clk_mplls),
+ .clk_plls = gxl_clk_plls,
+ .clk_plls_count = ARRAY_SIZE(gxl_clk_plls),
+ .clk_muxes = gxbb_clk_muxes,
+ .clk_muxes_count = ARRAY_SIZE(gxbb_clk_muxes),
+ .clk_dividers = gxbb_clk_dividers,
+ .clk_dividers_count = ARRAY_SIZE(gxbb_clk_dividers),
+ .clk_audio_dividers = gxbb_audio_dividers,
+ .clk_audio_dividers_count = ARRAY_SIZE(gxbb_audio_dividers),
+ .cpu_clk = &gxbb_cpu_clk,
+ .hw_onecell_data = &gxl_hw_onecell_data,
+};
+
+static const struct of_device_id clkc_match_table[] = {
+ { .compatible = "amlogic,gxbb-clkc", .data = &gxbb_clkc_data },
+ { .compatible = "amlogic,gxl-clkc", .data = &gxl_clkc_data },
+ {},
};
static int gxbb_clkc_probe(struct platform_device *pdev)
{
+ const struct clkc_data *clkc_data;
void __iomem *clk_base;
int ret, clkid, i;
struct clk_hw *parent_hw;
struct clk *parent_clk;
struct device *dev = &pdev->dev;
+ clkc_data = of_device_get_match_data(&pdev->dev);
+ if (!clkc_data)
+ return -EINVAL;
+
/* Generic clocks and PLLs */
clk_base = of_iomap(dev->of_node, 0);
if (!clk_base) {
@@ -918,34 +1495,45 @@ static int gxbb_clkc_probe(struct platform_device *pdev)
}
/* Populate base address for PLLs */
- for (i = 0; i < ARRAY_SIZE(gxbb_clk_plls); i++)
- gxbb_clk_plls[i]->base = clk_base;
+ for (i = 0; i < clkc_data->clk_plls_count; i++)
+ clkc_data->clk_plls[i]->base = clk_base;
/* Populate base address for MPLLs */
- for (i = 0; i < ARRAY_SIZE(gxbb_clk_mplls); i++)
- gxbb_clk_mplls[i]->base = clk_base;
+ for (i = 0; i < clkc_data->clk_mplls_count; i++)
+ clkc_data->clk_mplls[i]->base = clk_base;
/* Populate the base address for CPU clk */
- gxbb_cpu_clk.base = clk_base;
+ clkc_data->cpu_clk->base = clk_base;
+
+ /* Populate base address for gates */
+ for (i = 0; i < clkc_data->clk_gates_count; i++)
+ clkc_data->clk_gates[i]->reg = clk_base +
+ (u64)clkc_data->clk_gates[i]->reg;
- /* Populate the base address for the MPEG clks */
- gxbb_mpeg_clk_sel.reg = clk_base + (u64)gxbb_mpeg_clk_sel.reg;
- gxbb_mpeg_clk_div.reg = clk_base + (u64)gxbb_mpeg_clk_div.reg;
+ /* Populate base address for muxes */
+ for (i = 0; i < clkc_data->clk_muxes_count; i++)
+ clkc_data->clk_muxes[i]->reg = clk_base +
+ (u64)clkc_data->clk_muxes[i]->reg;
- /* Populate the base address for the SAR ADC clks */
- gxbb_sar_adc_clk_sel.reg = clk_base + (u64)gxbb_sar_adc_clk_sel.reg;
- gxbb_sar_adc_clk_div.reg = clk_base + (u64)gxbb_sar_adc_clk_div.reg;
+ /* Populate base address for dividers */
+ for (i = 0; i < clkc_data->clk_dividers_count; i++)
+ clkc_data->clk_dividers[i]->reg = clk_base +
+ (u64)clkc_data->clk_dividers[i]->reg;
- /* Populate base address for gates */
- for (i = 0; i < ARRAY_SIZE(gxbb_clk_gates); i++)
- gxbb_clk_gates[i]->reg = clk_base +
- (u64)gxbb_clk_gates[i]->reg;
+ /* Populate base address for the audio dividers */
+ for (i = 0; i < clkc_data->clk_audio_dividers_count; i++)
+ clkc_data->clk_audio_dividers[i]->base = clk_base;
/*
* register all clks
*/
- for (clkid = 0; clkid < NR_CLKS; clkid++) {
- ret = devm_clk_hw_register(dev, gxbb_hw_onecell_data.hws[clkid]);
+ for (clkid = 0; clkid < clkc_data->hw_onecell_data->num; clkid++) {
+ /* array might be sparse */
+ if (!clkc_data->hw_onecell_data->hws[clkid])
+ continue;
+
+ ret = devm_clk_hw_register(dev,
+ clkc_data->hw_onecell_data->hws[clkid]);
if (ret)
goto iounmap;
}
@@ -964,9 +1552,9 @@ static int gxbb_clkc_probe(struct platform_device *pdev)
* a new clk_hw, and this hack will no longer work. Releasing the ccr
* feature before that time solves the problem :-)
*/
- parent_hw = clk_hw_get_parent(&gxbb_cpu_clk.hw);
+ parent_hw = clk_hw_get_parent(&clkc_data->cpu_clk->hw);
parent_clk = parent_hw->clk;
- ret = clk_notifier_register(parent_clk, &gxbb_cpu_clk.clk_nb);
+ ret = clk_notifier_register(parent_clk, &clkc_data->cpu_clk->clk_nb);
if (ret) {
pr_err("%s: failed to register clock notifier for cpu_clk\n",
__func__);
@@ -974,23 +1562,18 @@ static int gxbb_clkc_probe(struct platform_device *pdev)
}
return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
- &gxbb_hw_onecell_data);
+ clkc_data->hw_onecell_data);
iounmap:
iounmap(clk_base);
return ret;
}
-static const struct of_device_id gxbb_clkc_match_table[] = {
- { .compatible = "amlogic,gxbb-clkc" },
- { }
-};
-
static struct platform_driver gxbb_driver = {
.probe = gxbb_clkc_probe,
.driver = {
.name = "gxbb-clkc",
- .of_match_table = gxbb_clkc_match_table,
+ .of_match_table = clkc_match_table,
},
};
diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h
index 945aefa4d251..93b8f07ee7af 100644
--- a/drivers/clk/meson/gxbb.h
+++ b/drivers/clk/meson/gxbb.h
@@ -71,6 +71,8 @@
#define HHI_GP0_PLL_CNTL2 0x44 /* 0x11 offset in data sheet */
#define HHI_GP0_PLL_CNTL3 0x48 /* 0x12 offset in data sheet */
#define HHI_GP0_PLL_CNTL4 0x4c /* 0x13 offset in data sheet */
+#define HHI_GP0_PLL_CNTL5 0x50 /* 0x14 offset in data sheet */
+#define HHI_GP0_PLL_CNTL1 0x58 /* 0x16 offset in data sheet */
#define HHI_XTAL_DIVN_CNTL 0xbc /* 0x2f offset in data sheet */
#define HHI_TIMER90K 0xec /* 0x3b offset in data sheet */
@@ -275,8 +277,15 @@
#define CLKID_MALI_1_DIV 104
/* CLKID_MALI_1 */
/* CLKID_MALI */
+#define CLKID_CTS_AMCLK 107
+#define CLKID_CTS_AMCLK_SEL 108
+#define CLKID_CTS_AMCLK_DIV 109
+#define CLKID_CTS_MCLK_I958 110
+#define CLKID_CTS_MCLK_I958_SEL 111
+#define CLKID_CTS_MCLK_I958_DIV 112
+#define CLKID_CTS_I958 113
-#define NR_CLKS 107
+#define NR_CLKS 114
/* include the CLKIDs that have been made part of the stable DT binding */
#include <dt-bindings/clock/gxbb-clkc.h>
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 888494d4fb8a..e9985503165c 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -245,6 +245,96 @@ static struct clk_fixed_factor meson8b_fclk_div7 = {
},
};
+static struct meson_clk_mpll meson8b_mpll0 = {
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 16,
+ .width = 9,
+ },
+ .en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 14,
+ .width = 1,
+ },
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct meson_clk_mpll meson8b_mpll1 = {
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 16,
+ .width = 9,
+ },
+ .en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 14,
+ .width = 1,
+ },
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct meson_clk_mpll meson8b_mpll2 = {
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 16,
+ .width = 9,
+ },
+ .en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 14,
+ .width = 1,
+ },
+ .lock = &clk_lock,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
/*
* FIXME cpu clocks and the legacy composite clocks (e.g. clk81) are both PLL
* post-dividers and should be modeled with their respective PLLs via the
@@ -491,6 +581,9 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
[CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
[CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
[CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
+ [CLKID_MPLL0] = &meson8b_mpll0.hw,
+ [CLKID_MPLL1] = &meson8b_mpll1.hw,
+ [CLKID_MPLL2] = &meson8b_mpll2.hw,
},
.num = CLK_NR_CLKS,
};
@@ -501,7 +594,13 @@ static struct meson_clk_pll *const meson8b_clk_plls[] = {
&meson8b_sys_pll,
};
-static struct clk_gate *meson8b_clk_gates[] = {
+static struct meson_clk_mpll *const meson8b_clk_mplls[] = {
+ &meson8b_mpll0,
+ &meson8b_mpll1,
+ &meson8b_mpll2,
+};
+
+static struct clk_gate *const meson8b_clk_gates[] = {
&meson8b_clk81,
&meson8b_ddr,
&meson8b_dos,
@@ -582,6 +681,14 @@ static struct clk_gate *meson8b_clk_gates[] = {
&meson8b_ao_iface,
};
+static struct clk_mux *const meson8b_clk_muxes[] = {
+ &meson8b_mpeg_clk_sel,
+};
+
+static struct clk_divider *const meson8b_clk_dividers[] = {
+ &meson8b_mpeg_clk_div,
+};
+
static int meson8b_clkc_probe(struct platform_device *pdev)
{
void __iomem *clk_base;
@@ -601,18 +708,28 @@ static int meson8b_clkc_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(meson8b_clk_plls); i++)
meson8b_clk_plls[i]->base = clk_base;
+ /* Populate base address for MPLLs */
+ for (i = 0; i < ARRAY_SIZE(meson8b_clk_mplls); i++)
+ meson8b_clk_mplls[i]->base = clk_base;
+
/* Populate the base address for CPU clk */
meson8b_cpu_clk.base = clk_base;
- /* Populate the base address for the MPEG clks */
- meson8b_mpeg_clk_sel.reg = clk_base + (u32)meson8b_mpeg_clk_sel.reg;
- meson8b_mpeg_clk_div.reg = clk_base + (u32)meson8b_mpeg_clk_div.reg;
-
/* Populate base address for gates */
for (i = 0; i < ARRAY_SIZE(meson8b_clk_gates); i++)
meson8b_clk_gates[i]->reg = clk_base +
(u32)meson8b_clk_gates[i]->reg;
+ /* Populate base address for muxes */
+ for (i = 0; i < ARRAY_SIZE(meson8b_clk_muxes); i++)
+ meson8b_clk_muxes[i]->reg = clk_base +
+ (u32)meson8b_clk_muxes[i]->reg;
+
+ /* Populate base address for dividers */
+ for (i = 0; i < ARRAY_SIZE(meson8b_clk_dividers); i++)
+ meson8b_clk_dividers[i]->reg = clk_base +
+ (u32)meson8b_clk_dividers[i]->reg;
+
/*
* register all clks
* CLKID_UNUSED = 0, so skip it and start with CLKID_XTAL = 1
diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h
index 010e9582888d..3881defc8644 100644
--- a/drivers/clk/meson/meson8b.h
+++ b/drivers/clk/meson/meson8b.h
@@ -42,6 +42,21 @@
#define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */
/*
+ * MPLL register offeset taken from the S905 datasheet. Vendor kernel source
+ * confirm these are the same for the S805.
+ */
+#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */
+#define HHI_MPLL_CNTL2 0x284 /* 0xa1 offset in data sheet */
+#define HHI_MPLL_CNTL3 0x288 /* 0xa2 offset in data sheet */
+#define HHI_MPLL_CNTL4 0x28C /* 0xa3 offset in data sheet */
+#define HHI_MPLL_CNTL5 0x290 /* 0xa4 offset in data sheet */
+#define HHI_MPLL_CNTL6 0x294 /* 0xa5 offset in data sheet */
+#define HHI_MPLL_CNTL7 0x298 /* 0xa6 offset in data sheet */
+#define HHI_MPLL_CNTL8 0x29C /* 0xa7 offset in data sheet */
+#define HHI_MPLL_CNTL9 0x2A0 /* 0xa8 offset in data sheet */
+#define HHI_MPLL_CNTL10 0x2A4 /* 0xa9 offset in data sheet */
+
+/*
* CLKID index values
*
* These indices are entirely contrived and do not map onto the hardware.
@@ -142,8 +157,11 @@
#define CLKID_AO_AHB_SRAM 90
#define CLKID_AO_AHB_BUS 91
#define CLKID_AO_IFACE 92
+#define CLKID_MPLL0 93
+#define CLKID_MPLL1 94
+#define CLKID_MPLL2 95
-#define CLK_NR_CLKS 93
+#define CLK_NR_CLKS 96
/* include the CLKIDs that have been made part of the stable DT binding */
#include <dt-bindings/clock/meson8b-clkc.h>