[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/Makefile b/src/kernel/linux/v4.14/drivers/clk/berlin/Makefile
new file mode 100644
index 0000000..2a36ab7
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/Makefile
@@ -0,0 +1,4 @@
+obj-y += berlin2-avpll.o berlin2-pll.o berlin2-div.o
+obj-$(CONFIG_MACH_BERLIN_BG2)	+= bg2.o
+obj-$(CONFIG_MACH_BERLIN_BG2CD)	+= bg2.o
+obj-$(CONFIG_MACH_BERLIN_BG2Q)	+= bg2q.o
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-avpll.c b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-avpll.c
new file mode 100644
index 0000000..cfcae46
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-avpll.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Alexandre Belloni <alexandre.belloni@free-electrons.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/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include "berlin2-avpll.h"
+
+/*
+ * Berlin2 SoCs comprise up to two PLLs called AVPLL built upon a
+ * VCO with 8 channels each, channel 8 is the odd-one-out and does
+ * not provide mul/div.
+ *
+ * Unfortunately, its registers are not named but just numbered. To
+ * get in at least some kind of structure, we split each AVPLL into
+ * the VCOs and each channel into separate clock drivers.
+ *
+ * Also, here and there the VCO registers are a bit different with
+ * respect to bit shifts. Make sure to add a comment for those.
+ */
+#define NUM_CHANNELS	8
+
+#define AVPLL_CTRL(x)		((x) * 0x4)
+
+#define VCO_CTRL0		AVPLL_CTRL(0)
+/* BG2/BG2CDs VCO_B has an additional shift of 4 for its VCO_CTRL0 reg */
+#define  VCO_RESET		BIT(0)
+#define  VCO_POWERUP		BIT(1)
+#define  VCO_INTERPOL_SHIFT	2
+#define  VCO_INTERPOL_MASK	(0xf << VCO_INTERPOL_SHIFT)
+#define  VCO_REG1V45_SEL_SHIFT	6
+#define  VCO_REG1V45_SEL(x)	((x) << VCO_REG1V45_SEL_SHIFT)
+#define  VCO_REG1V45_SEL_1V40	VCO_REG1V45_SEL(0)
+#define  VCO_REG1V45_SEL_1V45	VCO_REG1V45_SEL(1)
+#define  VCO_REG1V45_SEL_1V50	VCO_REG1V45_SEL(2)
+#define  VCO_REG1V45_SEL_1V55	VCO_REG1V45_SEL(3)
+#define  VCO_REG1V45_SEL_MASK	VCO_REG1V45_SEL(3)
+#define  VCO_REG0V9_SEL_SHIFT	8
+#define  VCO_REG0V9_SEL_MASK	(0xf << VCO_REG0V9_SEL_SHIFT)
+#define  VCO_VTHCAL_SHIFT	12
+#define  VCO_VTHCAL(x)		((x) << VCO_VTHCAL_SHIFT)
+#define  VCO_VTHCAL_0V90	VCO_VTHCAL(0)
+#define  VCO_VTHCAL_0V95	VCO_VTHCAL(1)
+#define  VCO_VTHCAL_1V00	VCO_VTHCAL(2)
+#define  VCO_VTHCAL_1V05	VCO_VTHCAL(3)
+#define  VCO_VTHCAL_MASK	VCO_VTHCAL(3)
+#define  VCO_KVCOEXT_SHIFT	14
+#define  VCO_KVCOEXT_MASK	(0x3 << VCO_KVCOEXT_SHIFT)
+#define  VCO_KVCOEXT_ENABLE	BIT(17)
+#define  VCO_V2IEXT_SHIFT	18
+#define  VCO_V2IEXT_MASK	(0xf << VCO_V2IEXT_SHIFT)
+#define  VCO_V2IEXT_ENABLE	BIT(22)
+#define  VCO_SPEED_SHIFT	23
+#define  VCO_SPEED(x)		((x) << VCO_SPEED_SHIFT)
+#define  VCO_SPEED_1G08_1G21	VCO_SPEED(0)
+#define  VCO_SPEED_1G21_1G40	VCO_SPEED(1)
+#define  VCO_SPEED_1G40_1G61	VCO_SPEED(2)
+#define  VCO_SPEED_1G61_1G86	VCO_SPEED(3)
+#define  VCO_SPEED_1G86_2G00	VCO_SPEED(4)
+#define  VCO_SPEED_2G00_2G22	VCO_SPEED(5)
+#define  VCO_SPEED_2G22		VCO_SPEED(6)
+#define  VCO_SPEED_MASK		VCO_SPEED(0x7)
+#define  VCO_CLKDET_ENABLE	BIT(26)
+#define VCO_CTRL1		AVPLL_CTRL(1)
+#define  VCO_REFDIV_SHIFT	0
+#define  VCO_REFDIV(x)		((x) << VCO_REFDIV_SHIFT)
+#define  VCO_REFDIV_1		VCO_REFDIV(0)
+#define  VCO_REFDIV_2		VCO_REFDIV(1)
+#define  VCO_REFDIV_4		VCO_REFDIV(2)
+#define  VCO_REFDIV_3		VCO_REFDIV(3)
+#define  VCO_REFDIV_MASK	VCO_REFDIV(0x3f)
+#define  VCO_FBDIV_SHIFT	6
+#define  VCO_FBDIV(x)		((x) << VCO_FBDIV_SHIFT)
+#define  VCO_FBDIV_MASK		VCO_FBDIV(0xff)
+#define  VCO_ICP_SHIFT		14
+/* PLL Charge Pump Current = 10uA * (x + 1) */
+#define  VCO_ICP(x)		((x) << VCO_ICP_SHIFT)
+#define  VCO_ICP_MASK		VCO_ICP(0xf)
+#define  VCO_LOAD_CAP		BIT(18)
+#define  VCO_CALIBRATION_START	BIT(19)
+#define VCO_FREQOFFSETn(x)	AVPLL_CTRL(3 + (x))
+#define  VCO_FREQOFFSET_MASK	0x7ffff
+#define VCO_CTRL10		AVPLL_CTRL(10)
+#define  VCO_POWERUP_CH1	BIT(20)
+#define VCO_CTRL11		AVPLL_CTRL(11)
+#define VCO_CTRL12		AVPLL_CTRL(12)
+#define VCO_CTRL13		AVPLL_CTRL(13)
+#define VCO_CTRL14		AVPLL_CTRL(14)
+#define VCO_CTRL15		AVPLL_CTRL(15)
+#define VCO_SYNC1n(x)		AVPLL_CTRL(15 + (x))
+#define  VCO_SYNC1_MASK		0x1ffff
+#define VCO_SYNC2n(x)		AVPLL_CTRL(23 + (x))
+#define  VCO_SYNC2_MASK		0x1ffff
+#define VCO_CTRL30		AVPLL_CTRL(30)
+#define  VCO_DPLL_CH1_ENABLE	BIT(17)
+
+struct berlin2_avpll_vco {
+	struct clk_hw hw;
+	void __iomem *base;
+	u8 flags;
+};
+
+#define to_avpll_vco(hw) container_of(hw, struct berlin2_avpll_vco, hw)
+
+static int berlin2_avpll_vco_is_enabled(struct clk_hw *hw)
+{
+	struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
+	u32 reg;
+
+	reg = readl_relaxed(vco->base + VCO_CTRL0);
+	if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
+		reg >>= 4;
+
+	return !!(reg & VCO_POWERUP);
+}
+
+static int berlin2_avpll_vco_enable(struct clk_hw *hw)
+{
+	struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
+	u32 reg;
+
+	reg = readl_relaxed(vco->base + VCO_CTRL0);
+	if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
+		reg |= VCO_POWERUP << 4;
+	else
+		reg |= VCO_POWERUP;
+	writel_relaxed(reg, vco->base + VCO_CTRL0);
+
+	return 0;
+}
+
+static void berlin2_avpll_vco_disable(struct clk_hw *hw)
+{
+	struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
+	u32 reg;
+
+	reg = readl_relaxed(vco->base + VCO_CTRL0);
+	if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
+		reg &= ~(VCO_POWERUP << 4);
+	else
+		reg &= ~VCO_POWERUP;
+	writel_relaxed(reg, vco->base + VCO_CTRL0);
+}
+
+static u8 vco_refdiv[] = { 1, 2, 4, 3 };
+
+static unsigned long
+berlin2_avpll_vco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
+	u32 reg, refdiv, fbdiv;
+	u64 freq = parent_rate;
+
+	/* AVPLL VCO frequency: Fvco = (Fref / refdiv) * fbdiv */
+	reg = readl_relaxed(vco->base + VCO_CTRL1);
+	refdiv = (reg & VCO_REFDIV_MASK) >> VCO_REFDIV_SHIFT;
+	refdiv = vco_refdiv[refdiv];
+	fbdiv = (reg & VCO_FBDIV_MASK) >> VCO_FBDIV_SHIFT;
+	freq *= fbdiv;
+	do_div(freq, refdiv);
+
+	return (unsigned long)freq;
+}
+
+static const struct clk_ops berlin2_avpll_vco_ops = {
+	.is_enabled	= berlin2_avpll_vco_is_enabled,
+	.enable		= berlin2_avpll_vco_enable,
+	.disable	= berlin2_avpll_vco_disable,
+	.recalc_rate	= berlin2_avpll_vco_recalc_rate,
+};
+
+int __init berlin2_avpll_vco_register(void __iomem *base,
+			       const char *name, const char *parent_name,
+			       u8 vco_flags, unsigned long flags)
+{
+	struct berlin2_avpll_vco *vco;
+	struct clk_init_data init;
+
+	vco = kzalloc(sizeof(*vco), GFP_KERNEL);
+	if (!vco)
+		return -ENOMEM;
+
+	vco->base = base;
+	vco->flags = vco_flags;
+	vco->hw.init = &init;
+	init.name = name;
+	init.ops = &berlin2_avpll_vco_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = flags;
+
+	return clk_hw_register(NULL, &vco->hw);
+}
+
+struct berlin2_avpll_channel {
+	struct clk_hw hw;
+	void __iomem *base;
+	u8 flags;
+	u8 index;
+};
+
+#define to_avpll_channel(hw) container_of(hw, struct berlin2_avpll_channel, hw)
+
+static int berlin2_avpll_channel_is_enabled(struct clk_hw *hw)
+{
+	struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
+	u32 reg;
+
+	if (ch->index == 7)
+		return 1;
+
+	reg = readl_relaxed(ch->base + VCO_CTRL10);
+	reg &= VCO_POWERUP_CH1 << ch->index;
+
+	return !!reg;
+}
+
+static int berlin2_avpll_channel_enable(struct clk_hw *hw)
+{
+	struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
+	u32 reg;
+
+	reg = readl_relaxed(ch->base + VCO_CTRL10);
+	reg |= VCO_POWERUP_CH1 << ch->index;
+	writel_relaxed(reg, ch->base + VCO_CTRL10);
+
+	return 0;
+}
+
+static void berlin2_avpll_channel_disable(struct clk_hw *hw)
+{
+	struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
+	u32 reg;
+
+	reg = readl_relaxed(ch->base + VCO_CTRL10);
+	reg &= ~(VCO_POWERUP_CH1 << ch->index);
+	writel_relaxed(reg, ch->base + VCO_CTRL10);
+}
+
+static const u8 div_hdmi[] = { 1, 2, 4, 6 };
+static const u8 div_av1[] = { 1, 2, 5, 5 };
+
+static unsigned long
+berlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
+	u32 reg, div_av2, div_av3, divider = 1;
+	u64 freq = parent_rate;
+
+	reg = readl_relaxed(ch->base + VCO_CTRL30);
+	if ((reg & (VCO_DPLL_CH1_ENABLE << ch->index)) == 0)
+		goto skip_div;
+
+	/*
+	 * Fch = (Fref * sync2) /
+	 *    (sync1 * div_hdmi * div_av1 * div_av2 * div_av3)
+	 */
+
+	reg = readl_relaxed(ch->base + VCO_SYNC1n(ch->index));
+	/* BG2/BG2CDs SYNC1 reg on AVPLL_B channel 1 is shifted by 4 */
+	if (ch->flags & BERLIN2_AVPLL_BIT_QUIRK && ch->index == 0)
+		reg >>= 4;
+	divider = reg & VCO_SYNC1_MASK;
+
+	reg = readl_relaxed(ch->base + VCO_SYNC2n(ch->index));
+	freq *= reg & VCO_SYNC2_MASK;
+
+	/* Channel 8 has no dividers */
+	if (ch->index == 7)
+		goto skip_div;
+
+	/*
+	 * HDMI divider start at VCO_CTRL11, bit 7; MSB is enable, lower 2 bit
+	 * determine divider.
+	 */
+	reg = readl_relaxed(ch->base + VCO_CTRL11) >> 7;
+	reg = (reg >> (ch->index * 3));
+	if (reg & BIT(2))
+		divider *= div_hdmi[reg & 0x3];
+
+	/*
+	 * AV1 divider start at VCO_CTRL11, bit 28; MSB is enable, lower 2 bit
+	 * determine divider.
+	 */
+	if (ch->index == 0) {
+		reg = readl_relaxed(ch->base + VCO_CTRL11);
+		reg >>= 28;
+	} else {
+		reg = readl_relaxed(ch->base + VCO_CTRL12);
+		reg >>= (ch->index-1) * 3;
+	}
+	if (reg & BIT(2))
+		divider *= div_av1[reg & 0x3];
+
+	/*
+	 * AV2 divider start at VCO_CTRL12, bit 18; each 7 bits wide,
+	 * zero is not a valid value.
+	 */
+	if (ch->index < 2) {
+		reg = readl_relaxed(ch->base + VCO_CTRL12);
+		reg >>= 18 + (ch->index * 7);
+	} else if (ch->index < 7) {
+		reg = readl_relaxed(ch->base + VCO_CTRL13);
+		reg >>= (ch->index - 2) * 7;
+	} else {
+		reg = readl_relaxed(ch->base + VCO_CTRL14);
+	}
+	div_av2 = reg & 0x7f;
+	if (div_av2)
+		divider *= div_av2;
+
+	/*
+	 * AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide.
+	 * AV2/AV3 form a fractional divider, where only specfic values for AV3
+	 * are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass.
+	 */
+	if (ch->index < 6) {
+		reg = readl_relaxed(ch->base + VCO_CTRL14);
+		reg >>= 7 + (ch->index * 4);
+	} else {
+		reg = readl_relaxed(ch->base + VCO_CTRL15);
+	}
+	div_av3 = reg & 0xf;
+	if (div_av2 && div_av3)
+		freq *= 2;
+
+skip_div:
+	do_div(freq, divider);
+	return (unsigned long)freq;
+}
+
+static const struct clk_ops berlin2_avpll_channel_ops = {
+	.is_enabled	= berlin2_avpll_channel_is_enabled,
+	.enable		= berlin2_avpll_channel_enable,
+	.disable	= berlin2_avpll_channel_disable,
+	.recalc_rate	= berlin2_avpll_channel_recalc_rate,
+};
+
+/*
+ * Another nice quirk:
+ * On some production SoCs, AVPLL channels are scrambled with respect
+ * to the channel numbering in the registers but still referenced by
+ * their original channel numbers. We deal with it by having a flag
+ * and a translation table for the index.
+ */
+static const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 };
+
+int __init berlin2_avpll_channel_register(void __iomem *base,
+			   const char *name, u8 index, const char *parent_name,
+			   u8 ch_flags, unsigned long flags)
+{
+	struct berlin2_avpll_channel *ch;
+	struct clk_init_data init;
+
+	ch = kzalloc(sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	ch->base = base;
+	if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK)
+		ch->index = quirk_index[index];
+	else
+		ch->index = index;
+
+	ch->flags = ch_flags;
+	ch->hw.init = &init;
+	init.name = name;
+	init.ops = &berlin2_avpll_channel_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = flags;
+
+	return clk_hw_register(NULL, &ch->hw);
+}
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-avpll.h b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-avpll.h
new file mode 100644
index 0000000..17e3111
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-avpll.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Alexandre Belloni <alexandre.belloni@free-electrons.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/>.
+ */
+#ifndef __BERLIN2_AVPLL_H
+#define __BERLIN2_AVPLL_H
+
+#define BERLIN2_AVPLL_BIT_QUIRK		BIT(0)
+#define BERLIN2_AVPLL_SCRAMBLE_QUIRK	BIT(1)
+
+int berlin2_avpll_vco_register(void __iomem *base, const char *name,
+	   const char *parent_name, u8 vco_flags, unsigned long flags);
+
+int berlin2_avpll_channel_register(void __iomem *base, const char *name,
+		       u8 index, const char *parent_name, u8 ch_flags,
+		       unsigned long flags);
+
+#endif /* __BERLIN2_AVPLL_H */
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-div.c b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-div.c
new file mode 100644
index 0000000..41ab2d3
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-div.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "berlin2-div.h"
+
+/*
+ * Clock dividers in Berlin2 SoCs comprise a complex cell to select
+ * input pll and divider. The virtual structure as it is used in Marvell
+ * BSP code can be seen as:
+ *
+ *                      +---+
+ * pll0 --------------->| 0 |                   +---+
+ *           +---+      |(B)|--+--------------->| 0 |      +---+
+ * pll1.0 -->| 0 |  +-->| 1 |  |   +--------+   |(E)|----->| 0 |   +---+
+ * pll1.1 -->| 1 |  |   +---+  +-->|(C) 1:M |-->| 1 |      |(F)|-->|(G)|->
+ * ...    -->|(A)|--+          |   +--------+   +---+  +-->| 1 |   +---+
+ * ...    -->|   |             +-->|(D) 1:3 |----------+   +---+
+ * pll1.N -->| N |                 +---------
+ *           +---+
+ *
+ * (A) input pll clock mux controlled by               <PllSelect[1:n]>
+ * (B) input pll bypass mux controlled by              <PllSwitch>
+ * (C) programmable clock divider controlled by        <Select[1:n]>
+ * (D) constant div-by-3 clock divider
+ * (E) programmable clock divider bypass controlled by <Switch>
+ * (F) constant div-by-3 clock mux controlled by       <D3Switch>
+ * (G) clock gate controlled by                        <Enable>
+ *
+ * For whatever reason, above control signals come in two flavors:
+ * - single register dividers with all bits in one register
+ * - shared register dividers with bits spread over multiple registers
+ *   (including signals for the same cell spread over consecutive registers)
+ *
+ * Also, clock gate and pll mux is not available on every div cell, so
+ * we have to deal with those, too. We reuse common clock composite driver
+ * for it.
+ */
+
+#define PLL_SELECT_MASK	0x7
+#define DIV_SELECT_MASK	0x7
+
+struct berlin2_div {
+	struct clk_hw hw;
+	void __iomem *base;
+	struct berlin2_div_map map;
+	spinlock_t *lock;
+};
+
+#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
+
+static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
+
+static int berlin2_div_is_enabled(struct clk_hw *hw)
+{
+	struct berlin2_div *div = to_berlin2_div(hw);
+	struct berlin2_div_map *map = &div->map;
+	u32 reg;
+
+	if (div->lock)
+		spin_lock(div->lock);
+
+	reg = readl_relaxed(div->base + map->gate_offs);
+	reg >>= map->gate_shift;
+
+	if (div->lock)
+		spin_unlock(div->lock);
+
+	return (reg & 0x1);
+}
+
+static int berlin2_div_enable(struct clk_hw *hw)
+{
+	struct berlin2_div *div = to_berlin2_div(hw);
+	struct berlin2_div_map *map = &div->map;
+	u32 reg;
+
+	if (div->lock)
+		spin_lock(div->lock);
+
+	reg = readl_relaxed(div->base + map->gate_offs);
+	reg |= BIT(map->gate_shift);
+	writel_relaxed(reg, div->base + map->gate_offs);
+
+	if (div->lock)
+		spin_unlock(div->lock);
+
+	return 0;
+}
+
+static void berlin2_div_disable(struct clk_hw *hw)
+{
+	struct berlin2_div *div = to_berlin2_div(hw);
+	struct berlin2_div_map *map = &div->map;
+	u32 reg;
+
+	if (div->lock)
+		spin_lock(div->lock);
+
+	reg = readl_relaxed(div->base + map->gate_offs);
+	reg &= ~BIT(map->gate_shift);
+	writel_relaxed(reg, div->base + map->gate_offs);
+
+	if (div->lock)
+		spin_unlock(div->lock);
+}
+
+static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct berlin2_div *div = to_berlin2_div(hw);
+	struct berlin2_div_map *map = &div->map;
+	u32 reg;
+
+	if (div->lock)
+		spin_lock(div->lock);
+
+	/* index == 0 is PLL_SWITCH */
+	reg = readl_relaxed(div->base + map->pll_switch_offs);
+	if (index == 0)
+		reg &= ~BIT(map->pll_switch_shift);
+	else
+		reg |= BIT(map->pll_switch_shift);
+	writel_relaxed(reg, div->base + map->pll_switch_offs);
+
+	/* index > 0 is PLL_SELECT */
+	if (index > 0) {
+		reg = readl_relaxed(div->base + map->pll_select_offs);
+		reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
+		reg |= (index - 1) << map->pll_select_shift;
+		writel_relaxed(reg, div->base + map->pll_select_offs);
+	}
+
+	if (div->lock)
+		spin_unlock(div->lock);
+
+	return 0;
+}
+
+static u8 berlin2_div_get_parent(struct clk_hw *hw)
+{
+	struct berlin2_div *div = to_berlin2_div(hw);
+	struct berlin2_div_map *map = &div->map;
+	u32 reg;
+	u8 index = 0;
+
+	if (div->lock)
+		spin_lock(div->lock);
+
+	/* PLL_SWITCH == 0 is index 0 */
+	reg = readl_relaxed(div->base + map->pll_switch_offs);
+	reg &= BIT(map->pll_switch_shift);
+	if (reg) {
+		reg = readl_relaxed(div->base + map->pll_select_offs);
+		reg >>= map->pll_select_shift;
+		reg &= PLL_SELECT_MASK;
+		index = 1 + reg;
+	}
+
+	if (div->lock)
+		spin_unlock(div->lock);
+
+	return index;
+}
+
+static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	struct berlin2_div *div = to_berlin2_div(hw);
+	struct berlin2_div_map *map = &div->map;
+	u32 divsw, div3sw, divider = 1;
+
+	if (div->lock)
+		spin_lock(div->lock);
+
+	divsw = readl_relaxed(div->base + map->div_switch_offs) &
+		(1 << map->div_switch_shift);
+	div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
+		(1 << map->div3_switch_shift);
+
+	/* constant divide-by-3 (dominant) */
+	if (div3sw != 0) {
+		divider = 3;
+	/* divider can be bypassed with DIV_SWITCH == 0 */
+	} else if (divsw == 0) {
+		divider = 1;
+	/* clock divider determined by DIV_SELECT */
+	} else {
+		u32 reg;
+		reg = readl_relaxed(div->base + map->div_select_offs);
+		reg >>= map->div_select_shift;
+		reg &= DIV_SELECT_MASK;
+		divider = clk_div[reg];
+	}
+
+	if (div->lock)
+		spin_unlock(div->lock);
+
+	return parent_rate / divider;
+}
+
+static const struct clk_ops berlin2_div_rate_ops = {
+	.recalc_rate	= berlin2_div_recalc_rate,
+};
+
+static const struct clk_ops berlin2_div_gate_ops = {
+	.is_enabled	= berlin2_div_is_enabled,
+	.enable		= berlin2_div_enable,
+	.disable	= berlin2_div_disable,
+};
+
+static const struct clk_ops berlin2_div_mux_ops = {
+	.set_parent	= berlin2_div_set_parent,
+	.get_parent	= berlin2_div_get_parent,
+};
+
+struct clk_hw * __init
+berlin2_div_register(const struct berlin2_div_map *map,
+		     void __iomem *base, const char *name, u8 div_flags,
+		     const char **parent_names, int num_parents,
+		     unsigned long flags, spinlock_t *lock)
+{
+	const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
+	const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
+	const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
+	struct berlin2_div *div;
+
+	div = kzalloc(sizeof(*div), GFP_KERNEL);
+	if (!div)
+		return ERR_PTR(-ENOMEM);
+
+	/* copy div_map to allow __initconst */
+	memcpy(&div->map, map, sizeof(*map));
+	div->base = base;
+	div->lock = lock;
+
+	if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
+		gate_ops = NULL;
+	if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
+		mux_ops = NULL;
+
+	return clk_hw_register_composite(NULL, name, parent_names, num_parents,
+				      &div->hw, mux_ops, &div->hw, rate_ops,
+				      &div->hw, gate_ops, flags);
+}
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-div.h b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-div.h
new file mode 100644
index 0000000..e835ddf
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-div.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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/>.
+ */
+#ifndef __BERLIN2_DIV_H
+#define __BERLIN2_DIV_H
+
+struct clk_hw;
+
+#define BERLIN2_DIV_HAS_GATE		BIT(0)
+#define BERLIN2_DIV_HAS_MUX		BIT(1)
+
+#define BERLIN2_PLL_SELECT(_off, _sh)	\
+	.pll_select_offs = _off,	\
+	.pll_select_shift = _sh
+
+#define BERLIN2_PLL_SWITCH(_off, _sh)	\
+	.pll_switch_offs = _off,	\
+	.pll_switch_shift = _sh
+
+#define BERLIN2_DIV_SELECT(_off, _sh)	\
+	.div_select_offs = _off,	\
+	.div_select_shift = _sh
+
+#define BERLIN2_DIV_SWITCH(_off, _sh)	\
+	.div_switch_offs = _off,	\
+	.div_switch_shift = _sh
+
+#define BERLIN2_DIV_D3SWITCH(_off, _sh)	\
+	.div3_switch_offs = _off,	\
+	.div3_switch_shift = _sh
+
+#define BERLIN2_DIV_GATE(_off, _sh)	\
+	.gate_offs = _off,		\
+	.gate_shift = _sh
+
+#define BERLIN2_SINGLE_DIV(_off)	\
+	BERLIN2_DIV_GATE(_off, 0),	\
+	BERLIN2_PLL_SELECT(_off, 1),	\
+	BERLIN2_PLL_SWITCH(_off, 4),	\
+	BERLIN2_DIV_SWITCH(_off, 5),	\
+	BERLIN2_DIV_D3SWITCH(_off, 6),	\
+	BERLIN2_DIV_SELECT(_off, 7)
+
+struct berlin2_div_map {
+	u16 pll_select_offs;
+	u16 pll_switch_offs;
+	u16 div_select_offs;
+	u16 div_switch_offs;
+	u16 div3_switch_offs;
+	u16 gate_offs;
+	u8 pll_select_shift;
+	u8 pll_switch_shift;
+	u8 div_select_shift;
+	u8 div_switch_shift;
+	u8 div3_switch_shift;
+	u8 gate_shift;
+};
+
+struct berlin2_div_data {
+	const char *name;
+	const u8 *parent_ids;
+	int num_parents;
+	unsigned long flags;
+	struct berlin2_div_map map;
+	u8 div_flags;
+};
+
+struct clk_hw *
+berlin2_div_register(const struct berlin2_div_map *map,
+	     void __iomem *base,  const char *name, u8 div_flags,
+	     const char **parent_names, int num_parents,
+	     unsigned long flags,  spinlock_t *lock);
+
+#endif /* __BERLIN2_DIV_H */
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-pll.c b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-pll.c
new file mode 100644
index 0000000..4ffbe80
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-pll.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "berlin2-div.h"
+#include "berlin2-pll.h"
+
+struct berlin2_pll {
+	struct clk_hw hw;
+	void __iomem *base;
+	struct berlin2_pll_map map;
+};
+
+#define to_berlin2_pll(hw) container_of(hw, struct berlin2_pll, hw)
+
+#define SPLL_CTRL0	0x00
+#define SPLL_CTRL1	0x04
+#define SPLL_CTRL2	0x08
+#define SPLL_CTRL3	0x0c
+#define SPLL_CTRL4	0x10
+
+#define FBDIV_MASK	0x1ff
+#define RFDIV_MASK	0x1f
+#define DIVSEL_MASK	0xf
+
+/*
+ * The output frequency formula for the pll is:
+ * clkout = fbdiv / refdiv * parent / vcodiv
+ */
+static unsigned long
+berlin2_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct berlin2_pll *pll = to_berlin2_pll(hw);
+	struct berlin2_pll_map *map = &pll->map;
+	u32 val, fbdiv, rfdiv, vcodivsel, vcodiv;
+	u64 rate = parent_rate;
+
+	val = readl_relaxed(pll->base + SPLL_CTRL0);
+	fbdiv = (val >> map->fbdiv_shift) & FBDIV_MASK;
+	rfdiv = (val >> map->rfdiv_shift) & RFDIV_MASK;
+	if (rfdiv == 0) {
+		pr_warn("%s has zero rfdiv\n", clk_hw_get_name(hw));
+		rfdiv = 1;
+	}
+
+	val = readl_relaxed(pll->base + SPLL_CTRL1);
+	vcodivsel = (val >> map->divsel_shift) & DIVSEL_MASK;
+	vcodiv = map->vcodiv[vcodivsel];
+	if (vcodiv == 0) {
+		pr_warn("%s has zero vcodiv (index %d)\n",
+			clk_hw_get_name(hw), vcodivsel);
+		vcodiv = 1;
+	}
+
+	rate *= fbdiv * map->mult;
+	do_div(rate, rfdiv * vcodiv);
+
+	return (unsigned long)rate;
+}
+
+static const struct clk_ops berlin2_pll_ops = {
+	.recalc_rate	= berlin2_pll_recalc_rate,
+};
+
+int __init
+berlin2_pll_register(const struct berlin2_pll_map *map,
+		     void __iomem *base, const char *name,
+		     const char *parent_name, unsigned long flags)
+{
+	struct clk_init_data init;
+	struct berlin2_pll *pll;
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return -ENOMEM;
+
+	/* copy pll_map to allow __initconst */
+	memcpy(&pll->map, map, sizeof(*map));
+	pll->base = base;
+	pll->hw.init = &init;
+	init.name = name;
+	init.ops = &berlin2_pll_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = flags;
+
+	return clk_hw_register(NULL, &pll->hw);
+}
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-pll.h b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-pll.h
new file mode 100644
index 0000000..583e024
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/berlin2-pll.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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/>.
+ */
+#ifndef __BERLIN2_PLL_H
+#define __BERLIN2_PLL_H
+
+struct berlin2_pll_map {
+	const u8 vcodiv[16];
+	u8 mult;
+	u8 fbdiv_shift;
+	u8 rfdiv_shift;
+	u8 divsel_shift;
+};
+
+int berlin2_pll_register(const struct berlin2_pll_map *map,
+			 void __iomem *base, const char *name,
+			 const char *parent_name, unsigned long flags);
+
+#endif /* __BERLIN2_PLL_H */
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/bg2.c b/src/kernel/linux/v4.14/drivers/clk/berlin/bg2.c
new file mode 100644
index 0000000..e7331ac
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/bg2.c
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Alexandre Belloni <alexandre.belloni@free-electrons.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.h>
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/berlin2.h>
+
+#include "berlin2-avpll.h"
+#include "berlin2-div.h"
+#include "berlin2-pll.h"
+#include "common.h"
+
+#define REG_PINMUX0		0x0000
+#define REG_PINMUX1		0x0004
+#define REG_SYSPLLCTL0		0x0014
+#define REG_SYSPLLCTL4		0x0024
+#define REG_MEMPLLCTL0		0x0028
+#define REG_MEMPLLCTL4		0x0038
+#define REG_CPUPLLCTL0		0x003c
+#define REG_CPUPLLCTL4		0x004c
+#define REG_AVPLLCTL0		0x0050
+#define REG_AVPLLCTL31		0x00cc
+#define REG_AVPLLCTL62		0x0148
+#define REG_PLLSTATUS		0x014c
+#define REG_CLKENABLE		0x0150
+#define REG_CLKSELECT0		0x0154
+#define REG_CLKSELECT1		0x0158
+#define REG_CLKSELECT2		0x015c
+#define REG_CLKSELECT3		0x0160
+#define REG_CLKSWITCH0		0x0164
+#define REG_CLKSWITCH1		0x0168
+#define REG_RESET_TRIGGER	0x0178
+#define REG_RESET_STATUS0	0x017c
+#define REG_RESET_STATUS1	0x0180
+#define REG_SW_GENERIC0		0x0184
+#define REG_SW_GENERIC3		0x0190
+#define REG_PRODUCTID		0x01cc
+#define REG_PRODUCTID_EXT	0x01d0
+#define REG_GFX3DCORE_CLKCTL	0x022c
+#define REG_GFX3DSYS_CLKCTL	0x0230
+#define REG_ARC_CLKCTL		0x0234
+#define REG_VIP_CLKCTL		0x0238
+#define REG_SDIO0XIN_CLKCTL	0x023c
+#define REG_SDIO1XIN_CLKCTL	0x0240
+#define REG_GFX3DEXTRA_CLKCTL	0x0244
+#define REG_GFX3D_RESET		0x0248
+#define REG_GC360_CLKCTL	0x024c
+#define REG_SDIO_DLLMST_CLKCTL	0x0250
+
+/*
+ * BG2/BG2CD SoCs have the following audio/video I/O units:
+ *
+ * audiohd: HDMI TX audio
+ * audio0:  7.1ch TX
+ * audio1:  2ch TX
+ * audio2:  2ch RX
+ * audio3:  SPDIF TX
+ * video0:  HDMI video
+ * video1:  Secondary video
+ * video2:  SD auxiliary video
+ *
+ * There are no external audio clocks (ACLKI0, ACLKI1) and
+ * only one external video clock (VCLKI0).
+ *
+ * Currently missing bits and pieces:
+ * - audio_fast_pll is unknown
+ * - audiohd_pll is unknown
+ * - video0_pll is unknown
+ * - audio[023], audiohd parent pll is assumed to be audio_fast_pll
+ *
+ */
+
+#define	MAX_CLKS 41
+static struct clk_hw_onecell_data *clk_data;
+static DEFINE_SPINLOCK(lock);
+static void __iomem *gbase;
+
+enum {
+	REFCLK, VIDEO_EXT0,
+	SYSPLL, MEMPLL, CPUPLL,
+	AVPLL_A1, AVPLL_A2, AVPLL_A3, AVPLL_A4,
+	AVPLL_A5, AVPLL_A6, AVPLL_A7, AVPLL_A8,
+	AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
+	AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
+	AUDIO1_PLL, AUDIO_FAST_PLL,
+	VIDEO0_PLL, VIDEO0_IN,
+	VIDEO1_PLL, VIDEO1_IN,
+	VIDEO2_PLL, VIDEO2_IN,
+};
+
+static const char *clk_names[] = {
+	[REFCLK]		= "refclk",
+	[VIDEO_EXT0]		= "video_ext0",
+	[SYSPLL]		= "syspll",
+	[MEMPLL]		= "mempll",
+	[CPUPLL]		= "cpupll",
+	[AVPLL_A1]		= "avpll_a1",
+	[AVPLL_A2]		= "avpll_a2",
+	[AVPLL_A3]		= "avpll_a3",
+	[AVPLL_A4]		= "avpll_a4",
+	[AVPLL_A5]		= "avpll_a5",
+	[AVPLL_A6]		= "avpll_a6",
+	[AVPLL_A7]		= "avpll_a7",
+	[AVPLL_A8]		= "avpll_a8",
+	[AVPLL_B1]		= "avpll_b1",
+	[AVPLL_B2]		= "avpll_b2",
+	[AVPLL_B3]		= "avpll_b3",
+	[AVPLL_B4]		= "avpll_b4",
+	[AVPLL_B5]		= "avpll_b5",
+	[AVPLL_B6]		= "avpll_b6",
+	[AVPLL_B7]		= "avpll_b7",
+	[AVPLL_B8]		= "avpll_b8",
+	[AUDIO1_PLL]		= "audio1_pll",
+	[AUDIO_FAST_PLL]	= "audio_fast_pll",
+	[VIDEO0_PLL]		= "video0_pll",
+	[VIDEO0_IN]		= "video0_in",
+	[VIDEO1_PLL]		= "video1_pll",
+	[VIDEO1_IN]		= "video1_in",
+	[VIDEO2_PLL]		= "video2_pll",
+	[VIDEO2_IN]		= "video2_in",
+};
+
+static const struct berlin2_pll_map bg2_pll_map __initconst = {
+	.vcodiv		= {10, 15, 20, 25, 30, 40, 50, 60, 80},
+	.mult		= 10,
+	.fbdiv_shift	= 6,
+	.rfdiv_shift	= 1,
+	.divsel_shift	= 7,
+};
+
+static const u8 default_parent_ids[] = {
+	SYSPLL, AVPLL_B4, AVPLL_A5, AVPLL_B6, AVPLL_B7, SYSPLL
+};
+
+static const struct berlin2_div_data bg2_divs[] __initconst = {
+	{
+		.name = "sys",
+		.parent_ids = (const u8 []){
+			SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
+		},
+		.num_parents = 6,
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	{
+		.name = "cpu",
+		.parent_ids = (const u8 []){
+			CPUPLL, MEMPLL, MEMPLL, MEMPLL, MEMPLL
+		},
+		.num_parents = 5,
+		.map = {
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
+		},
+		.div_flags = BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "drmfigo",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 16),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 17),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 20),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "cfg",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 23),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 26),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "gfx",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 29),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 0),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "zsp",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 5),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 3),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 6),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "perif",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 9),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 12),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	{
+		.name = "pcube",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 15),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 18),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "vscope",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 21),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 24),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "nfc_ecc",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 18),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 27),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 0),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "vpp",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT2, 3),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 6),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 4),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 5),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 6),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "app",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT2, 9),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 12),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 7),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 8),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 9),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "audio0",
+		.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
+		.num_parents = 1,
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 22),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 17),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 10),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 11),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE,
+		.flags = 0,
+	},
+	{
+		.name = "audio2",
+		.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
+		.num_parents = 1,
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 24),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 20),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 14),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 15),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE,
+		.flags = 0,
+	},
+	{
+		.name = "audio3",
+		.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
+		.num_parents = 1,
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 25),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 23),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 16),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 17),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE,
+		.flags = 0,
+	},
+	{
+		.name = "audio1",
+		.parent_ids = (const u8 []){ AUDIO1_PLL },
+		.num_parents = 1,
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 23),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT3, 0),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 12),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 13),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE,
+		.flags = 0,
+	},
+	{
+		.name = "gfx3d_core",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_GFX3DCORE_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "gfx3d_sys",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_GFX3DSYS_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "arc",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_ARC_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "vip",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_VIP_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "sdio0xin",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "sdio1xin",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "gfx3d_extra",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_GFX3DEXTRA_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "gc360",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_GC360_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "sdio_dllmst",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_SDIO_DLLMST_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+};
+
+static const struct berlin2_gate_data bg2_gates[] __initconst = {
+	{ "geth0",	"perif",	7 },
+	{ "geth1",	"perif",	8 },
+	{ "sata",	"perif",	9 },
+	{ "ahbapb",	"perif",	10, CLK_IGNORE_UNUSED },
+	{ "usb0",	"perif",	11 },
+	{ "usb1",	"perif",	12 },
+	{ "pbridge",	"perif",	13, CLK_IGNORE_UNUSED },
+	{ "sdio0",	"perif",	14 },
+	{ "sdio1",	"perif",	15 },
+	{ "nfc",	"perif",	17 },
+	{ "smemc",	"perif",	19 },
+	{ "audiohd",	"audiohd_pll",	26 },
+	{ "video0",	"video0_in",	27 },
+	{ "video1",	"video1_in",	28 },
+	{ "video2",	"video2_in",	29 },
+};
+
+static void __init berlin2_clock_setup(struct device_node *np)
+{
+	struct device_node *parent_np = of_get_parent(np);
+	const char *parent_names[9];
+	struct clk *clk;
+	struct clk_hw *hw;
+	struct clk_hw **hws;
+	u8 avpll_flags = 0;
+	int n, ret;
+
+	clk_data = kzalloc(sizeof(*clk_data) +
+			   sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL);
+	if (!clk_data)
+		return;
+	clk_data->num = MAX_CLKS;
+	hws = clk_data->hws;
+
+	gbase = of_iomap(parent_np, 0);
+	if (!gbase)
+		return;
+
+	/* overwrite default clock names with DT provided ones */
+	clk = of_clk_get_by_name(np, clk_names[REFCLK]);
+	if (!IS_ERR(clk)) {
+		clk_names[REFCLK] = __clk_get_name(clk);
+		clk_put(clk);
+	}
+
+	clk = of_clk_get_by_name(np, clk_names[VIDEO_EXT0]);
+	if (!IS_ERR(clk)) {
+		clk_names[VIDEO_EXT0] = __clk_get_name(clk);
+		clk_put(clk);
+	}
+
+	/* simple register PLLs */
+	ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0,
+				   clk_names[SYSPLL], clk_names[REFCLK], 0);
+	if (ret)
+		goto bg2_fail;
+
+	ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0,
+				   clk_names[MEMPLL], clk_names[REFCLK], 0);
+	if (ret)
+		goto bg2_fail;
+
+	ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0,
+				   clk_names[CPUPLL], clk_names[REFCLK], 0);
+	if (ret)
+		goto bg2_fail;
+
+	if (of_device_is_compatible(np, "marvell,berlin2-global-register"))
+		avpll_flags |= BERLIN2_AVPLL_SCRAMBLE_QUIRK;
+
+	/* audio/video VCOs */
+	ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA",
+			 clk_names[REFCLK], avpll_flags, 0);
+	if (ret)
+		goto bg2_fail;
+
+	for (n = 0; n < 8; n++) {
+		ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0,
+			     clk_names[AVPLL_A1 + n], n, "avpll_vcoA",
+			     avpll_flags, 0);
+		if (ret)
+			goto bg2_fail;
+	}
+
+	ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB",
+				 clk_names[REFCLK], BERLIN2_AVPLL_BIT_QUIRK |
+				 avpll_flags, 0);
+	if (ret)
+		goto bg2_fail;
+
+	for (n = 0; n < 8; n++) {
+		ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31,
+			     clk_names[AVPLL_B1 + n], n, "avpll_vcoB",
+			     BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, 0);
+		if (ret)
+			goto bg2_fail;
+	}
+
+	/* reference clock bypass switches */
+	parent_names[0] = clk_names[SYSPLL];
+	parent_names[1] = clk_names[REFCLK];
+	hw = clk_hw_register_mux(NULL, "syspll_byp", parent_names, 2,
+			       0, gbase + REG_CLKSWITCH0, 0, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+	clk_names[SYSPLL] = clk_hw_get_name(hw);
+
+	parent_names[0] = clk_names[MEMPLL];
+	parent_names[1] = clk_names[REFCLK];
+	hw = clk_hw_register_mux(NULL, "mempll_byp", parent_names, 2,
+			       0, gbase + REG_CLKSWITCH0, 1, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+	clk_names[MEMPLL] = clk_hw_get_name(hw);
+
+	parent_names[0] = clk_names[CPUPLL];
+	parent_names[1] = clk_names[REFCLK];
+	hw = clk_hw_register_mux(NULL, "cpupll_byp", parent_names, 2,
+			       0, gbase + REG_CLKSWITCH0, 2, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+	clk_names[CPUPLL] = clk_hw_get_name(hw);
+
+	/* clock muxes */
+	parent_names[0] = clk_names[AVPLL_B3];
+	parent_names[1] = clk_names[AVPLL_A3];
+	hw = clk_hw_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2,
+			       0, gbase + REG_CLKSELECT2, 29, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+
+	parent_names[0] = clk_names[VIDEO0_PLL];
+	parent_names[1] = clk_names[VIDEO_EXT0];
+	hw = clk_hw_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2,
+			       0, gbase + REG_CLKSELECT3, 4, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+
+	parent_names[0] = clk_names[VIDEO1_PLL];
+	parent_names[1] = clk_names[VIDEO_EXT0];
+	hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2,
+			       0, gbase + REG_CLKSELECT3, 6, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+
+	parent_names[0] = clk_names[AVPLL_A2];
+	parent_names[1] = clk_names[AVPLL_B2];
+	hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2,
+			       0, gbase + REG_CLKSELECT3, 7, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+
+	parent_names[0] = clk_names[VIDEO2_PLL];
+	parent_names[1] = clk_names[VIDEO_EXT0];
+	hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2,
+			       0, gbase + REG_CLKSELECT3, 9, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+
+	parent_names[0] = clk_names[AVPLL_B1];
+	parent_names[1] = clk_names[AVPLL_A5];
+	hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2,
+			       0, gbase + REG_CLKSELECT3, 10, 1, 0, &lock);
+	if (IS_ERR(hw))
+		goto bg2_fail;
+
+	/* clock divider cells */
+	for (n = 0; n < ARRAY_SIZE(bg2_divs); n++) {
+		const struct berlin2_div_data *dd = &bg2_divs[n];
+		int k;
+
+		for (k = 0; k < dd->num_parents; k++)
+			parent_names[k] = clk_names[dd->parent_ids[k]];
+
+		hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
+				dd->name, dd->div_flags, parent_names,
+				dd->num_parents, dd->flags, &lock);
+	}
+
+	/* clock gate cells */
+	for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) {
+		const struct berlin2_gate_data *gd = &bg2_gates[n];
+
+		hws[CLKID_GETH0 + n] = clk_hw_register_gate(NULL, gd->name,
+			    gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
+			    gd->bit_idx, 0, &lock);
+	}
+
+	/* twdclk is derived from cpu/3 */
+	hws[CLKID_TWD] =
+		clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
+
+	/* check for errors on leaf clocks */
+	for (n = 0; n < MAX_CLKS; n++) {
+		if (!IS_ERR(hws[n]))
+			continue;
+
+		pr_err("%pOF: Unable to register leaf clock %d\n", np, n);
+		goto bg2_fail;
+	}
+
+	/* register clk-provider */
+	of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+
+	return;
+
+bg2_fail:
+	iounmap(gbase);
+}
+CLK_OF_DECLARE(berlin2_clk, "marvell,berlin2-clk",
+	       berlin2_clock_setup);
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/bg2q.c b/src/kernel/linux/v4.14/drivers/clk/berlin/bg2q.c
new file mode 100644
index 0000000..67c270b
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/bg2q.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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.h>
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/berlin2q.h>
+
+#include "berlin2-div.h"
+#include "berlin2-pll.h"
+#include "common.h"
+
+#define REG_PINMUX0		0x0018
+#define REG_PINMUX5		0x002c
+#define REG_SYSPLLCTL0		0x0030
+#define REG_SYSPLLCTL4		0x0040
+#define REG_CLKENABLE		0x00e8
+#define REG_CLKSELECT0		0x00ec
+#define REG_CLKSELECT1		0x00f0
+#define REG_CLKSELECT2		0x00f4
+#define REG_CLKSWITCH0		0x00f8
+#define REG_CLKSWITCH1		0x00fc
+#define REG_SW_GENERIC0		0x0110
+#define REG_SW_GENERIC3		0x011c
+#define REG_SDIO0XIN_CLKCTL	0x0158
+#define REG_SDIO1XIN_CLKCTL	0x015c
+
+#define	MAX_CLKS 28
+static struct clk_hw_onecell_data *clk_data;
+static DEFINE_SPINLOCK(lock);
+static void __iomem *gbase;
+static void __iomem *cpupll_base;
+
+enum {
+	REFCLK,
+	SYSPLL, CPUPLL,
+	AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
+	AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
+};
+
+static const char *clk_names[] = {
+	[REFCLK]		= "refclk",
+	[SYSPLL]		= "syspll",
+	[CPUPLL]		= "cpupll",
+	[AVPLL_B1]		= "avpll_b1",
+	[AVPLL_B2]		= "avpll_b2",
+	[AVPLL_B3]		= "avpll_b3",
+	[AVPLL_B4]		= "avpll_b4",
+	[AVPLL_B5]		= "avpll_b5",
+	[AVPLL_B6]		= "avpll_b6",
+	[AVPLL_B7]		= "avpll_b7",
+	[AVPLL_B8]		= "avpll_b8",
+};
+
+static const struct berlin2_pll_map bg2q_pll_map __initconst = {
+	.vcodiv		= {1, 0, 2, 0, 3, 4, 0, 6, 8},
+	.mult		= 1,
+	.fbdiv_shift	= 7,
+	.rfdiv_shift	= 2,
+	.divsel_shift	= 9,
+};
+
+static const u8 default_parent_ids[] = {
+	SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
+};
+
+static const struct berlin2_div_data bg2q_divs[] __initconst = {
+	{
+		.name = "sys",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	{
+		.name = "drmfigo",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 17),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "cfg",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "gfx2d",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "zsp",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "perif",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 7),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	{
+		.name = "pcube",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "vscope",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "nfc_ecc",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 19),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "vpp",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "app",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
+			BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0),
+			BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3),
+			BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
+			BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
+			BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "sdio0xin",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+	{
+		.name = "sdio1xin",
+		.parent_ids = default_parent_ids,
+		.num_parents = ARRAY_SIZE(default_parent_ids),
+		.map = {
+			BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
+		},
+		.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
+		.flags = 0,
+	},
+};
+
+static const struct berlin2_gate_data bg2q_gates[] __initconst = {
+	{ "gfx2daxi",	"perif",	5 },
+	{ "geth0",	"perif",	8 },
+	{ "sata",	"perif",	9 },
+	{ "ahbapb",	"perif",	10, CLK_IGNORE_UNUSED },
+	{ "usb0",	"perif",	11 },
+	{ "usb1",	"perif",	12 },
+	{ "usb2",	"perif",	13 },
+	{ "usb3",	"perif",	14 },
+	{ "pbridge",	"perif",	15, CLK_IGNORE_UNUSED },
+	{ "sdio",	"perif",	16 },
+	{ "nfc",	"perif",	18 },
+	{ "pcie",	"perif",	22 },
+};
+
+static void __init berlin2q_clock_setup(struct device_node *np)
+{
+	struct device_node *parent_np = of_get_parent(np);
+	const char *parent_names[9];
+	struct clk *clk;
+	struct clk_hw **hws;
+	int n, ret;
+
+	clk_data = kzalloc(sizeof(*clk_data) +
+			   sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL);
+	if (!clk_data)
+		return;
+	clk_data->num = MAX_CLKS;
+	hws = clk_data->hws;
+
+	gbase = of_iomap(parent_np, 0);
+	if (!gbase) {
+		pr_err("%pOF: Unable to map global base\n", np);
+		return;
+	}
+
+	/* BG2Q CPU PLL is not part of global registers */
+	cpupll_base = of_iomap(parent_np, 1);
+	if (!cpupll_base) {
+		pr_err("%pOF: Unable to map cpupll base\n", np);
+		iounmap(gbase);
+		return;
+	}
+
+	/* overwrite default clock names with DT provided ones */
+	clk = of_clk_get_by_name(np, clk_names[REFCLK]);
+	if (!IS_ERR(clk)) {
+		clk_names[REFCLK] = __clk_get_name(clk);
+		clk_put(clk);
+	}
+
+	/* simple register PLLs */
+	ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
+				   clk_names[SYSPLL], clk_names[REFCLK], 0);
+	if (ret)
+		goto bg2q_fail;
+
+	ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
+				   clk_names[CPUPLL], clk_names[REFCLK], 0);
+	if (ret)
+		goto bg2q_fail;
+
+	/* TODO: add BG2Q AVPLL */
+
+	/*
+	 * TODO: add reference clock bypass switches:
+	 * memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass
+	 */
+
+	/* clock divider cells */
+	for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) {
+		const struct berlin2_div_data *dd = &bg2q_divs[n];
+		int k;
+
+		for (k = 0; k < dd->num_parents; k++)
+			parent_names[k] = clk_names[dd->parent_ids[k]];
+
+		hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
+				dd->name, dd->div_flags, parent_names,
+				dd->num_parents, dd->flags, &lock);
+	}
+
+	/* clock gate cells */
+	for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) {
+		const struct berlin2_gate_data *gd = &bg2q_gates[n];
+
+		hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name,
+			    gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
+			    gd->bit_idx, 0, &lock);
+	}
+
+	/* cpuclk divider is fixed to 1 */
+	hws[CLKID_CPU] =
+		clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
+					  0, 1, 1);
+	/* twdclk is derived from cpu/3 */
+	hws[CLKID_TWD] =
+		clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
+
+	/* check for errors on leaf clocks */
+	for (n = 0; n < MAX_CLKS; n++) {
+		if (!IS_ERR(hws[n]))
+			continue;
+
+		pr_err("%pOF: Unable to register leaf clock %d\n", np, n);
+		goto bg2q_fail;
+	}
+
+	/* register clk-provider */
+	of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+
+	return;
+
+bg2q_fail:
+	iounmap(cpupll_base);
+	iounmap(gbase);
+}
+CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk",
+	       berlin2q_clock_setup);
diff --git a/src/kernel/linux/v4.14/drivers/clk/berlin/common.h b/src/kernel/linux/v4.14/drivers/clk/berlin/common.h
new file mode 100644
index 0000000..bc68a14
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/berlin/common.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Alexandre Belloni <alexandre.belloni@free-electrons.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/>.
+ */
+#ifndef __BERLIN2_COMMON_H
+#define __BERLIN2_COMMON_H
+
+struct berlin2_gate_data {
+	const char *name;
+	const char *parent_name;
+	u8 bit_idx;
+	unsigned long flags;
+};
+
+#endif /* BERLIN2_COMMON_H */