[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/clk/pistachio/Makefile b/src/kernel/linux/v4.14/drivers/clk/pistachio/Makefile
new file mode 100644
index 0000000..f1e151f
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/pistachio/Makefile
@@ -0,0 +1,3 @@
+obj-y	+= clk.o
+obj-y	+= clk-pll.o
+obj-y	+= clk-pistachio.o
diff --git a/src/kernel/linux/v4.14/drivers/clk/pistachio/clk-pistachio.c b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk-pistachio.c
new file mode 100644
index 0000000..c4ceb5e
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk-pistachio.c
@@ -0,0 +1,338 @@
+/*
+ * Pistachio SoC clock controllers
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pistachio-clk.h>
+
+#include "clk.h"
+
+static struct pistachio_gate pistachio_gates[] __initdata = {
+	GATE(CLK_MIPS, "mips", "mips_div", 0x104, 0),
+	GATE(CLK_AUDIO_IN, "audio_in", "audio_clk_in_gate", 0x104, 1),
+	GATE(CLK_AUDIO, "audio", "audio_div", 0x104, 2),
+	GATE(CLK_I2S, "i2s", "i2s_div", 0x104, 3),
+	GATE(CLK_SPDIF, "spdif", "spdif_div", 0x104, 4),
+	GATE(CLK_AUDIO_DAC, "audio_dac", "audio_dac_div", 0x104, 5),
+	GATE(CLK_RPU_V, "rpu_v", "rpu_v_div", 0x104, 6),
+	GATE(CLK_RPU_L, "rpu_l", "rpu_l_div", 0x104, 7),
+	GATE(CLK_RPU_SLEEP, "rpu_sleep", "rpu_sleep_div", 0x104, 8),
+	GATE(CLK_WIFI_PLL_GATE, "wifi_pll_gate", "wifi_pll_mux", 0x104, 9),
+	GATE(CLK_RPU_CORE, "rpu_core", "rpu_core_div", 0x104, 10),
+	GATE(CLK_WIFI_ADC, "wifi_adc", "wifi_div8_mux", 0x104, 11),
+	GATE(CLK_WIFI_DAC, "wifi_dac", "wifi_div4_mux", 0x104, 12),
+	GATE(CLK_USB_PHY, "usb_phy", "usb_phy_div", 0x104, 13),
+	GATE(CLK_ENET_IN, "enet_in", "enet_clk_in_gate", 0x104, 14),
+	GATE(CLK_ENET, "enet", "enet_div", 0x104, 15),
+	GATE(CLK_UART0, "uart0", "uart0_div", 0x104, 16),
+	GATE(CLK_UART1, "uart1", "uart1_div", 0x104, 17),
+	GATE(CLK_PERIPH_SYS, "periph_sys", "sys_internal_div", 0x104, 18),
+	GATE(CLK_SPI0, "spi0", "spi0_div", 0x104, 19),
+	GATE(CLK_SPI1, "spi1", "spi1_div", 0x104, 20),
+	GATE(CLK_EVENT_TIMER, "event_timer", "event_timer_div", 0x104, 21),
+	GATE(CLK_AUX_ADC_INTERNAL, "aux_adc_internal", "sys_internal_div",
+	     0x104, 22),
+	GATE(CLK_AUX_ADC, "aux_adc", "aux_adc_div", 0x104, 23),
+	GATE(CLK_SD_HOST, "sd_host", "sd_host_div", 0x104, 24),
+	GATE(CLK_BT, "bt", "bt_div", 0x104, 25),
+	GATE(CLK_BT_DIV4, "bt_div4", "bt_div4_div", 0x104, 26),
+	GATE(CLK_BT_DIV8, "bt_div8", "bt_div8_div", 0x104, 27),
+	GATE(CLK_BT_1MHZ, "bt_1mhz", "bt_1mhz_div", 0x104, 28),
+};
+
+static struct pistachio_fixed_factor pistachio_ffs[] __initdata = {
+	FIXED_FACTOR(CLK_WIFI_DIV4, "wifi_div4", "wifi_pll", 4),
+	FIXED_FACTOR(CLK_WIFI_DIV8, "wifi_div8", "wifi_pll", 8),
+};
+
+static struct pistachio_div pistachio_divs[] __initdata = {
+	DIV(CLK_MIPS_INTERNAL_DIV, "mips_internal_div", "mips_pll_mux",
+	    0x204, 2),
+	DIV(CLK_MIPS_DIV, "mips_div", "mips_internal_div", 0x208, 8),
+	DIV_F(CLK_AUDIO_DIV, "audio_div", "audio_mux",
+		0x20c, 8, CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(CLK_I2S_DIV, "i2s_div", "audio_pll_mux",
+		0x210, 8, CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(CLK_SPDIF_DIV, "spdif_div", "audio_pll_mux",
+		0x214, 8, CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(CLK_AUDIO_DAC_DIV, "audio_dac_div", "audio_pll_mux",
+		0x218, 8, CLK_DIVIDER_ROUND_CLOSEST),
+	DIV(CLK_RPU_V_DIV, "rpu_v_div", "rpu_v_pll_mux", 0x21c, 2),
+	DIV(CLK_RPU_L_DIV, "rpu_l_div", "rpu_l_mux", 0x220, 2),
+	DIV(CLK_RPU_SLEEP_DIV, "rpu_sleep_div", "xtal", 0x224, 10),
+	DIV(CLK_RPU_CORE_DIV, "rpu_core_div", "rpu_core_mux", 0x228, 3),
+	DIV(CLK_USB_PHY_DIV, "usb_phy_div", "sys_internal_div", 0x22c, 6),
+	DIV(CLK_ENET_DIV, "enet_div", "enet_mux", 0x230, 6),
+	DIV_F(CLK_UART0_INTERNAL_DIV, "uart0_internal_div", "sys_pll_mux",
+	      0x234, 3, CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(CLK_UART0_DIV, "uart0_div", "uart0_internal_div", 0x238, 10,
+	      CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(CLK_UART1_INTERNAL_DIV, "uart1_internal_div", "sys_pll_mux",
+	      0x23c, 3, CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(CLK_UART1_DIV, "uart1_div", "uart1_internal_div", 0x240, 10,
+	      CLK_DIVIDER_ROUND_CLOSEST),
+	DIV(CLK_SYS_INTERNAL_DIV, "sys_internal_div", "sys_pll_mux", 0x244, 3),
+	DIV(CLK_SPI0_INTERNAL_DIV, "spi0_internal_div", "sys_pll_mux",
+	    0x248, 3),
+	DIV(CLK_SPI0_DIV, "spi0_div", "spi0_internal_div", 0x24c, 7),
+	DIV(CLK_SPI1_INTERNAL_DIV, "spi1_internal_div", "sys_pll_mux",
+	    0x250, 3),
+	DIV(CLK_SPI1_DIV, "spi1_div", "spi1_internal_div", 0x254, 7),
+	DIV(CLK_EVENT_TIMER_INTERNAL_DIV, "event_timer_internal_div",
+	    "event_timer_mux", 0x258, 3),
+	DIV(CLK_EVENT_TIMER_DIV, "event_timer_div", "event_timer_internal_div",
+	    0x25c, 12),
+	DIV(CLK_AUX_ADC_INTERNAL_DIV, "aux_adc_internal_div",
+	    "aux_adc_internal", 0x260, 3),
+	DIV(CLK_AUX_ADC_DIV, "aux_adc_div", "aux_adc_internal_div", 0x264, 10),
+	DIV(CLK_SD_HOST_DIV, "sd_host_div", "sd_host_mux", 0x268, 6),
+	DIV(CLK_BT_DIV, "bt_div", "bt_pll_mux", 0x26c, 6),
+	DIV(CLK_BT_DIV4_DIV, "bt_div4_div", "bt_pll_mux", 0x270, 6),
+	DIV(CLK_BT_DIV8_DIV, "bt_div8_div", "bt_pll_mux", 0x274, 6),
+	DIV(CLK_BT_1MHZ_INTERNAL_DIV, "bt_1mhz_internal_div", "bt_pll_mux",
+	    0x278, 3),
+	DIV(CLK_BT_1MHZ_DIV, "bt_1mhz_div", "bt_1mhz_internal_div", 0x27c, 10),
+};
+
+PNAME(mux_xtal_audio_refclk) = { "xtal", "audio_clk_in_gate" };
+PNAME(mux_xtal_mips) = { "xtal", "mips_pll" };
+PNAME(mux_xtal_audio) = { "xtal", "audio_pll", "audio_in" };
+PNAME(mux_audio_debug) = { "audio_pll_mux", "debug_mux" };
+PNAME(mux_xtal_rpu_v) = { "xtal", "rpu_v_pll" };
+PNAME(mux_xtal_rpu_l) = { "xtal", "rpu_l_pll" };
+PNAME(mux_rpu_l_mips) = { "rpu_l_pll_mux", "mips_pll_mux" };
+PNAME(mux_xtal_wifi) = { "xtal", "wifi_pll" };
+PNAME(mux_xtal_wifi_div4) = { "xtal", "wifi_div4" };
+PNAME(mux_xtal_wifi_div8) = { "xtal", "wifi_div8" };
+PNAME(mux_wifi_div4_rpu_l) = { "wifi_pll_gate", "wifi_div4_mux",
+			       "rpu_l_pll_mux" };
+PNAME(mux_xtal_sys) = { "xtal", "sys_pll" };
+PNAME(mux_sys_enet) = { "sys_internal_div", "enet_in" };
+PNAME(mux_audio_sys) = { "audio_pll_mux", "sys_internal_div" };
+PNAME(mux_sys_bt) = { "sys_internal_div", "bt_pll_mux" };
+PNAME(mux_xtal_bt) = { "xtal", "bt_pll" };
+
+static struct pistachio_mux pistachio_muxes[] __initdata = {
+	MUX(CLK_AUDIO_REF_MUX, "audio_refclk_mux", mux_xtal_audio_refclk,
+	    0x200, 0),
+	MUX(CLK_MIPS_PLL_MUX, "mips_pll_mux", mux_xtal_mips, 0x200, 1),
+	MUX(CLK_AUDIO_PLL_MUX, "audio_pll_mux", mux_xtal_audio, 0x200, 2),
+	MUX(CLK_AUDIO_MUX, "audio_mux", mux_audio_debug, 0x200, 4),
+	MUX(CLK_RPU_V_PLL_MUX, "rpu_v_pll_mux", mux_xtal_rpu_v, 0x200, 5),
+	MUX(CLK_RPU_L_PLL_MUX, "rpu_l_pll_mux", mux_xtal_rpu_l, 0x200, 6),
+	MUX(CLK_RPU_L_MUX, "rpu_l_mux", mux_rpu_l_mips, 0x200, 7),
+	MUX(CLK_WIFI_PLL_MUX, "wifi_pll_mux", mux_xtal_wifi, 0x200, 8),
+	MUX(CLK_WIFI_DIV4_MUX, "wifi_div4_mux", mux_xtal_wifi_div4, 0x200, 9),
+	MUX(CLK_WIFI_DIV8_MUX, "wifi_div8_mux", mux_xtal_wifi_div8, 0x200, 10),
+	MUX(CLK_RPU_CORE_MUX, "rpu_core_mux", mux_wifi_div4_rpu_l, 0x200, 11),
+	MUX(CLK_SYS_PLL_MUX, "sys_pll_mux", mux_xtal_sys, 0x200, 13),
+	MUX(CLK_ENET_MUX, "enet_mux", mux_sys_enet, 0x200, 14),
+	MUX(CLK_EVENT_TIMER_MUX, "event_timer_mux", mux_audio_sys, 0x200, 15),
+	MUX(CLK_SD_HOST_MUX, "sd_host_mux", mux_sys_bt, 0x200, 16),
+	MUX(CLK_BT_PLL_MUX, "bt_pll_mux", mux_xtal_bt, 0x200, 17),
+};
+
+static struct pistachio_pll pistachio_plls[] __initdata = {
+	PLL_FIXED(CLK_MIPS_PLL, "mips_pll", "xtal", PLL_GF40LP_LAINT, 0x0),
+	PLL_FIXED(CLK_AUDIO_PLL, "audio_pll", "audio_refclk_mux",
+		  PLL_GF40LP_FRAC, 0xc),
+	PLL_FIXED(CLK_RPU_V_PLL, "rpu_v_pll", "xtal", PLL_GF40LP_LAINT, 0x20),
+	PLL_FIXED(CLK_RPU_L_PLL, "rpu_l_pll", "xtal", PLL_GF40LP_LAINT, 0x2c),
+	PLL_FIXED(CLK_SYS_PLL, "sys_pll", "xtal", PLL_GF40LP_FRAC, 0x38),
+	PLL_FIXED(CLK_WIFI_PLL, "wifi_pll", "xtal", PLL_GF40LP_FRAC, 0x4c),
+	PLL_FIXED(CLK_BT_PLL, "bt_pll", "xtal", PLL_GF40LP_LAINT, 0x60),
+};
+
+PNAME(mux_debug) = { "mips_pll_mux", "rpu_v_pll_mux",
+		     "rpu_l_pll_mux", "sys_pll_mux",
+		     "wifi_pll_mux", "bt_pll_mux" };
+static u32 mux_debug_idx[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10 };
+
+static unsigned int pistachio_critical_clks_core[] __initdata = {
+	CLK_MIPS
+};
+
+static unsigned int pistachio_critical_clks_sys[] __initdata = {
+	PERIPH_CLK_SYS,
+	PERIPH_CLK_SYS_BUS,
+	PERIPH_CLK_DDR,
+	PERIPH_CLK_ROM,
+};
+
+static void __init pistachio_clk_init(struct device_node *np)
+{
+	struct pistachio_clk_provider *p;
+	struct clk *debug_clk;
+
+	p = pistachio_clk_alloc_provider(np, CLK_NR_CLKS);
+	if (!p)
+		return;
+
+	pistachio_clk_register_pll(p, pistachio_plls,
+				   ARRAY_SIZE(pistachio_plls));
+	pistachio_clk_register_mux(p, pistachio_muxes,
+				   ARRAY_SIZE(pistachio_muxes));
+	pistachio_clk_register_div(p, pistachio_divs,
+				   ARRAY_SIZE(pistachio_divs));
+	pistachio_clk_register_fixed_factor(p, pistachio_ffs,
+					    ARRAY_SIZE(pistachio_ffs));
+	pistachio_clk_register_gate(p, pistachio_gates,
+				    ARRAY_SIZE(pistachio_gates));
+
+	debug_clk = clk_register_mux_table(NULL, "debug_mux", mux_debug,
+					   ARRAY_SIZE(mux_debug),
+					   CLK_SET_RATE_NO_REPARENT,
+					   p->base + 0x200, 18, 0x1f, 0,
+					   mux_debug_idx, NULL);
+	p->clk_data.clks[CLK_DEBUG_MUX] = debug_clk;
+
+	pistachio_clk_register_provider(p);
+
+	pistachio_clk_force_enable(p, pistachio_critical_clks_core,
+				   ARRAY_SIZE(pistachio_critical_clks_core));
+}
+CLK_OF_DECLARE(pistachio_clk, "img,pistachio-clk", pistachio_clk_init);
+
+static struct pistachio_gate pistachio_periph_gates[] __initdata = {
+	GATE(PERIPH_CLK_SYS, "sys", "periph_sys", 0x100, 0),
+	GATE(PERIPH_CLK_SYS_BUS, "bus_sys", "periph_sys", 0x100, 1),
+	GATE(PERIPH_CLK_DDR, "ddr", "periph_sys", 0x100, 2),
+	GATE(PERIPH_CLK_ROM, "rom", "rom_div", 0x100, 3),
+	GATE(PERIPH_CLK_COUNTER_FAST, "counter_fast", "counter_fast_div",
+	     0x100, 4),
+	GATE(PERIPH_CLK_COUNTER_SLOW, "counter_slow", "counter_slow_div",
+	     0x100, 5),
+	GATE(PERIPH_CLK_IR, "ir", "ir_div", 0x100, 6),
+	GATE(PERIPH_CLK_WD, "wd", "wd_div", 0x100, 7),
+	GATE(PERIPH_CLK_PDM, "pdm", "pdm_div", 0x100, 8),
+	GATE(PERIPH_CLK_PWM, "pwm", "pwm_div", 0x100, 9),
+	GATE(PERIPH_CLK_I2C0, "i2c0", "i2c0_div", 0x100, 10),
+	GATE(PERIPH_CLK_I2C1, "i2c1", "i2c1_div", 0x100, 11),
+	GATE(PERIPH_CLK_I2C2, "i2c2", "i2c2_div", 0x100, 12),
+	GATE(PERIPH_CLK_I2C3, "i2c3", "i2c3_div", 0x100, 13),
+};
+
+static struct pistachio_div pistachio_periph_divs[] __initdata = {
+	DIV(PERIPH_CLK_ROM_DIV, "rom_div", "periph_sys", 0x10c, 7),
+	DIV(PERIPH_CLK_COUNTER_FAST_DIV, "counter_fast_div", "periph_sys",
+	    0x110, 7),
+	DIV(PERIPH_CLK_COUNTER_SLOW_PRE_DIV, "counter_slow_pre_div",
+	    "periph_sys", 0x114, 7),
+	DIV(PERIPH_CLK_COUNTER_SLOW_DIV, "counter_slow_div",
+	    "counter_slow_pre_div", 0x118, 7),
+	DIV_F(PERIPH_CLK_IR_PRE_DIV, "ir_pre_div", "periph_sys", 0x11c, 7,
+	      CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(PERIPH_CLK_IR_DIV, "ir_div", "ir_pre_div", 0x120, 7,
+	      CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(PERIPH_CLK_WD_PRE_DIV, "wd_pre_div", "periph_sys", 0x124, 7,
+	      CLK_DIVIDER_ROUND_CLOSEST),
+	DIV_F(PERIPH_CLK_WD_DIV, "wd_div", "wd_pre_div", 0x128, 7,
+	      CLK_DIVIDER_ROUND_CLOSEST),
+	DIV(PERIPH_CLK_PDM_PRE_DIV, "pdm_pre_div", "periph_sys", 0x12c, 7),
+	DIV(PERIPH_CLK_PDM_DIV, "pdm_div", "pdm_pre_div", 0x130, 7),
+	DIV(PERIPH_CLK_PWM_PRE_DIV, "pwm_pre_div", "periph_sys", 0x134, 7),
+	DIV(PERIPH_CLK_PWM_DIV, "pwm_div", "pwm_pre_div", 0x138, 7),
+	DIV(PERIPH_CLK_I2C0_PRE_DIV, "i2c0_pre_div", "periph_sys", 0x13c, 7),
+	DIV(PERIPH_CLK_I2C0_DIV, "i2c0_div", "i2c0_pre_div", 0x140, 7),
+	DIV(PERIPH_CLK_I2C1_PRE_DIV, "i2c1_pre_div", "periph_sys", 0x144, 7),
+	DIV(PERIPH_CLK_I2C1_DIV, "i2c1_div", "i2c1_pre_div", 0x148, 7),
+	DIV(PERIPH_CLK_I2C2_PRE_DIV, "i2c2_pre_div", "periph_sys", 0x14c, 7),
+	DIV(PERIPH_CLK_I2C2_DIV, "i2c2_div", "i2c2_pre_div", 0x150, 7),
+	DIV(PERIPH_CLK_I2C3_PRE_DIV, "i2c3_pre_div", "periph_sys", 0x154, 7),
+	DIV(PERIPH_CLK_I2C3_DIV, "i2c3_div", "i2c3_pre_div", 0x158, 7),
+};
+
+static void __init pistachio_clk_periph_init(struct device_node *np)
+{
+	struct pistachio_clk_provider *p;
+
+	p = pistachio_clk_alloc_provider(np, PERIPH_CLK_NR_CLKS);
+	if (!p)
+		return;
+
+	pistachio_clk_register_div(p, pistachio_periph_divs,
+				   ARRAY_SIZE(pistachio_periph_divs));
+	pistachio_clk_register_gate(p, pistachio_periph_gates,
+				    ARRAY_SIZE(pistachio_periph_gates));
+
+	pistachio_clk_register_provider(p);
+
+	pistachio_clk_force_enable(p, pistachio_critical_clks_sys,
+				   ARRAY_SIZE(pistachio_critical_clks_sys));
+}
+CLK_OF_DECLARE(pistachio_clk_periph, "img,pistachio-clk-periph",
+	       pistachio_clk_periph_init);
+
+static struct pistachio_gate pistachio_sys_gates[] __initdata = {
+	GATE(SYS_CLK_I2C0, "i2c0_sys", "sys", 0x8, 0),
+	GATE(SYS_CLK_I2C1, "i2c1_sys", "sys", 0x8, 1),
+	GATE(SYS_CLK_I2C2, "i2c2_sys", "sys", 0x8, 2),
+	GATE(SYS_CLK_I2C3, "i2c3_sys", "sys", 0x8, 3),
+	GATE(SYS_CLK_I2S_IN, "i2s_in_sys", "sys", 0x8, 4),
+	GATE(SYS_CLK_PAUD_OUT, "paud_out_sys", "sys", 0x8, 5),
+	GATE(SYS_CLK_SPDIF_OUT, "spdif_out_sys", "sys", 0x8, 6),
+	GATE(SYS_CLK_SPI0_MASTER, "spi0_master_sys", "sys", 0x8, 7),
+	GATE(SYS_CLK_SPI0_SLAVE, "spi0_slave_sys", "sys", 0x8, 8),
+	GATE(SYS_CLK_PWM, "pwm_sys", "sys", 0x8, 9),
+	GATE(SYS_CLK_UART0, "uart0_sys", "sys", 0x8, 10),
+	GATE(SYS_CLK_UART1, "uart1_sys", "sys", 0x8, 11),
+	GATE(SYS_CLK_SPI1, "spi1_sys", "sys", 0x8, 12),
+	GATE(SYS_CLK_MDC, "mdc_sys", "sys", 0x8, 13),
+	GATE(SYS_CLK_SD_HOST, "sd_host_sys", "sys", 0x8, 14),
+	GATE(SYS_CLK_ENET, "enet_sys", "sys", 0x8, 15),
+	GATE(SYS_CLK_IR, "ir_sys", "sys", 0x8, 16),
+	GATE(SYS_CLK_WD, "wd_sys", "sys", 0x8, 17),
+	GATE(SYS_CLK_TIMER, "timer_sys", "sys", 0x8, 18),
+	GATE(SYS_CLK_I2S_OUT, "i2s_out_sys", "sys", 0x8, 24),
+	GATE(SYS_CLK_SPDIF_IN, "spdif_in_sys", "sys", 0x8, 25),
+	GATE(SYS_CLK_EVENT_TIMER, "event_timer_sys", "sys", 0x8, 26),
+	GATE(SYS_CLK_HASH, "hash_sys", "sys", 0x8, 27),
+};
+
+static void __init pistachio_cr_periph_init(struct device_node *np)
+{
+	struct pistachio_clk_provider *p;
+
+	p = pistachio_clk_alloc_provider(np, SYS_CLK_NR_CLKS);
+	if (!p)
+		return;
+
+	pistachio_clk_register_gate(p, pistachio_sys_gates,
+				    ARRAY_SIZE(pistachio_sys_gates));
+
+	pistachio_clk_register_provider(p);
+}
+CLK_OF_DECLARE(pistachio_cr_periph, "img,pistachio-cr-periph",
+	       pistachio_cr_periph_init);
+
+static struct pistachio_gate pistachio_ext_gates[] __initdata = {
+	GATE(EXT_CLK_ENET_IN, "enet_clk_in_gate", "enet_clk_in", 0x58, 5),
+	GATE(EXT_CLK_AUDIO_IN, "audio_clk_in_gate", "audio_clk_in", 0x58, 8)
+};
+
+static void __init pistachio_cr_top_init(struct device_node *np)
+{
+	struct pistachio_clk_provider *p;
+
+	p = pistachio_clk_alloc_provider(np, EXT_CLK_NR_CLKS);
+	if (!p)
+		return;
+
+	pistachio_clk_register_gate(p, pistachio_ext_gates,
+				    ARRAY_SIZE(pistachio_ext_gates));
+
+	pistachio_clk_register_provider(p);
+}
+CLK_OF_DECLARE(pistachio_cr_top, "img,pistachio-cr-top",
+	       pistachio_cr_top_init);
diff --git a/src/kernel/linux/v4.14/drivers/clk/pistachio/clk-pll.c b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk-pll.c
new file mode 100644
index 0000000..7e8daab
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk-pll.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define PLL_STATUS			0x0
+#define PLL_STATUS_LOCK			BIT(0)
+
+#define PLL_CTRL1			0x4
+#define PLL_CTRL1_REFDIV_SHIFT		0
+#define PLL_CTRL1_REFDIV_MASK		0x3f
+#define PLL_CTRL1_FBDIV_SHIFT		6
+#define PLL_CTRL1_FBDIV_MASK		0xfff
+#define PLL_INT_CTRL1_POSTDIV1_SHIFT	18
+#define PLL_INT_CTRL1_POSTDIV1_MASK	0x7
+#define PLL_INT_CTRL1_POSTDIV2_SHIFT	21
+#define PLL_INT_CTRL1_POSTDIV2_MASK	0x7
+#define PLL_INT_CTRL1_PD		BIT(24)
+#define PLL_INT_CTRL1_DSMPD		BIT(25)
+#define PLL_INT_CTRL1_FOUTPOSTDIVPD	BIT(26)
+#define PLL_INT_CTRL1_FOUTVCOPD		BIT(27)
+
+#define PLL_CTRL2			0x8
+#define PLL_FRAC_CTRL2_FRAC_SHIFT	0
+#define PLL_FRAC_CTRL2_FRAC_MASK	0xffffff
+#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT	24
+#define PLL_FRAC_CTRL2_POSTDIV1_MASK	0x7
+#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT	27
+#define PLL_FRAC_CTRL2_POSTDIV2_MASK	0x7
+#define PLL_INT_CTRL2_BYPASS		BIT(28)
+
+#define PLL_CTRL3			0xc
+#define PLL_FRAC_CTRL3_PD		BIT(0)
+#define PLL_FRAC_CTRL3_DACPD		BIT(1)
+#define PLL_FRAC_CTRL3_DSMPD		BIT(2)
+#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD	BIT(3)
+#define PLL_FRAC_CTRL3_FOUT4PHASEPD	BIT(4)
+#define PLL_FRAC_CTRL3_FOUTVCOPD	BIT(5)
+
+#define PLL_CTRL4			0x10
+#define PLL_FRAC_CTRL4_BYPASS		BIT(28)
+
+#define MIN_PFD				9600000UL
+#define MIN_VCO_LA			400000000UL
+#define MAX_VCO_LA			1600000000UL
+#define MIN_VCO_FRAC_INT		600000000UL
+#define MAX_VCO_FRAC_INT		1600000000UL
+#define MIN_VCO_FRAC_FRAC		600000000UL
+#define MAX_VCO_FRAC_FRAC		2400000000UL
+#define MIN_OUTPUT_LA			8000000UL
+#define MAX_OUTPUT_LA			1600000000UL
+#define MIN_OUTPUT_FRAC			12000000UL
+#define MAX_OUTPUT_FRAC			1600000000UL
+
+/* Fractional PLL operating modes */
+enum pll_mode {
+	PLL_MODE_FRAC,
+	PLL_MODE_INT,
+};
+
+struct pistachio_clk_pll {
+	struct clk_hw hw;
+	void __iomem *base;
+	struct pistachio_pll_rate_table *rates;
+	unsigned int nr_rates;
+};
+
+static inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg)
+{
+	return readl(pll->base + reg);
+}
+
+static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg)
+{
+	writel(val, pll->base + reg);
+}
+
+static inline void pll_lock(struct pistachio_clk_pll *pll)
+{
+	while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))
+		cpu_relax();
+}
+
+static inline u64 do_div_round_closest(u64 dividend, u64 divisor)
+{
+	dividend += divisor / 2;
+	return div64_u64(dividend, divisor);
+}
+
+static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)
+{
+	return container_of(hw, struct pistachio_clk_pll, hw);
+}
+
+static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val;
+
+	val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD;
+	return val ? PLL_MODE_INT : PLL_MODE_FRAC;
+}
+
+static inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val;
+
+	val = pll_readl(pll, PLL_CTRL3);
+	if (mode == PLL_MODE_INT)
+		val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD;
+	else
+		val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD);
+
+	pll_writel(pll, val, PLL_CTRL3);
+}
+
+static struct pistachio_pll_rate_table *
+pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref,
+	       unsigned long fout)
+{
+	unsigned int i;
+
+	for (i = 0; i < pll->nr_rates; i++) {
+		if (pll->rates[i].fref == fref && pll->rates[i].fout == fout)
+			return &pll->rates[i];
+	}
+
+	return NULL;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long *parent_rate)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	unsigned int i;
+
+	for (i = 0; i < pll->nr_rates; i++) {
+		if (i > 0 && pll->rates[i].fref == *parent_rate &&
+		    pll->rates[i].fout <= rate)
+			return pll->rates[i - 1].fout;
+	}
+
+	return pll->rates[0].fout;
+}
+
+static int pll_gf40lp_frac_enable(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val;
+
+	val = pll_readl(pll, PLL_CTRL3);
+	val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD |
+		 PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD);
+	pll_writel(pll, val, PLL_CTRL3);
+
+	val = pll_readl(pll, PLL_CTRL4);
+	val &= ~PLL_FRAC_CTRL4_BYPASS;
+	pll_writel(pll, val, PLL_CTRL4);
+
+	pll_lock(pll);
+
+	return 0;
+}
+
+static void pll_gf40lp_frac_disable(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val;
+
+	val = pll_readl(pll, PLL_CTRL3);
+	val |= PLL_FRAC_CTRL3_PD;
+	pll_writel(pll, val, PLL_CTRL3);
+}
+
+static int pll_gf40lp_frac_is_enabled(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+
+	return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD);
+}
+
+static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
+				    unsigned long parent_rate)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	struct pistachio_pll_rate_table *params;
+	int enabled = pll_gf40lp_frac_is_enabled(hw);
+	u64 val, vco, old_postdiv1, old_postdiv2;
+	const char *name = clk_hw_get_name(hw);
+
+	if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC)
+		return -EINVAL;
+
+	params = pll_get_params(pll, parent_rate, rate);
+	if (!params || !params->refdiv)
+		return -EINVAL;
+
+	/* calculate vco */
+	vco = params->fref;
+	vco *= (params->fbdiv << 24) + params->frac;
+	vco = div64_u64(vco, params->refdiv << 24);
+
+	if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC)
+		pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco,
+			MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC);
+
+	val = div64_u64(params->fref, params->refdiv);
+	if (val < MIN_PFD)
+		pr_warn("%s: PFD %llu is too low (min %lu)\n",
+			name, val, MIN_PFD);
+	if (val > vco / 16)
+		pr_warn("%s: PFD %llu is too high (max %llu)\n",
+			name, val, vco / 16);
+
+	val = pll_readl(pll, PLL_CTRL1);
+	val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |
+		 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT));
+	val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |
+		(params->fbdiv << PLL_CTRL1_FBDIV_SHIFT);
+	pll_writel(pll, val, PLL_CTRL1);
+
+	val = pll_readl(pll, PLL_CTRL2);
+
+	old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &
+		       PLL_FRAC_CTRL2_POSTDIV1_MASK;
+	old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &
+		       PLL_FRAC_CTRL2_POSTDIV2_MASK;
+	if (enabled &&
+	    (params->postdiv1 != old_postdiv1 ||
+	     params->postdiv2 != old_postdiv2))
+		pr_warn("%s: changing postdiv while PLL is enabled\n", name);
+
+	if (params->postdiv2 > params->postdiv1)
+		pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);
+
+	val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) |
+		 (PLL_FRAC_CTRL2_POSTDIV1_MASK <<
+		  PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |
+		 (PLL_FRAC_CTRL2_POSTDIV2_MASK <<
+		  PLL_FRAC_CTRL2_POSTDIV2_SHIFT));
+	val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) |
+		(params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |
+		(params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT);
+	pll_writel(pll, val, PLL_CTRL2);
+
+	/* set operating mode */
+	if (params->frac)
+		pll_frac_set_mode(hw, PLL_MODE_FRAC);
+	else
+		pll_frac_set_mode(hw, PLL_MODE_INT);
+
+	if (enabled)
+		pll_lock(pll);
+
+	return 0;
+}
+
+static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,
+						 unsigned long parent_rate)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate;
+
+	val = pll_readl(pll, PLL_CTRL1);
+	prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;
+	fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;
+
+	val = pll_readl(pll, PLL_CTRL2);
+	postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &
+		PLL_FRAC_CTRL2_POSTDIV1_MASK;
+	postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &
+		PLL_FRAC_CTRL2_POSTDIV2_MASK;
+	frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK;
+
+	/* get operating mode (int/frac) and calculate rate accordingly */
+	rate = parent_rate;
+	if (pll_frac_get_mode(hw) == PLL_MODE_FRAC)
+		rate *= (fbdiv << 24) + frac;
+	else
+		rate *= (fbdiv << 24);
+
+	rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24);
+
+	return rate;
+}
+
+static struct clk_ops pll_gf40lp_frac_ops = {
+	.enable = pll_gf40lp_frac_enable,
+	.disable = pll_gf40lp_frac_disable,
+	.is_enabled = pll_gf40lp_frac_is_enabled,
+	.recalc_rate = pll_gf40lp_frac_recalc_rate,
+	.round_rate = pll_round_rate,
+	.set_rate = pll_gf40lp_frac_set_rate,
+};
+
+static struct clk_ops pll_gf40lp_frac_fixed_ops = {
+	.enable = pll_gf40lp_frac_enable,
+	.disable = pll_gf40lp_frac_disable,
+	.is_enabled = pll_gf40lp_frac_is_enabled,
+	.recalc_rate = pll_gf40lp_frac_recalc_rate,
+};
+
+static int pll_gf40lp_laint_enable(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val;
+
+	val = pll_readl(pll, PLL_CTRL1);
+	val &= ~(PLL_INT_CTRL1_PD |
+		 PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD);
+	pll_writel(pll, val, PLL_CTRL1);
+
+	val = pll_readl(pll, PLL_CTRL2);
+	val &= ~PLL_INT_CTRL2_BYPASS;
+	pll_writel(pll, val, PLL_CTRL2);
+
+	pll_lock(pll);
+
+	return 0;
+}
+
+static void pll_gf40lp_laint_disable(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val;
+
+	val = pll_readl(pll, PLL_CTRL1);
+	val |= PLL_INT_CTRL1_PD;
+	pll_writel(pll, val, PLL_CTRL1);
+}
+
+static int pll_gf40lp_laint_is_enabled(struct clk_hw *hw)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+
+	return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD);
+}
+
+static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long parent_rate)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	struct pistachio_pll_rate_table *params;
+	int enabled = pll_gf40lp_laint_is_enabled(hw);
+	u32 val, vco, old_postdiv1, old_postdiv2;
+	const char *name = clk_hw_get_name(hw);
+
+	if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA)
+		return -EINVAL;
+
+	params = pll_get_params(pll, parent_rate, rate);
+	if (!params || !params->refdiv)
+		return -EINVAL;
+
+	vco = div_u64(params->fref * params->fbdiv, params->refdiv);
+	if (vco < MIN_VCO_LA || vco > MAX_VCO_LA)
+		pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,
+			MIN_VCO_LA, MAX_VCO_LA);
+
+	val = div_u64(params->fref, params->refdiv);
+	if (val < MIN_PFD)
+		pr_warn("%s: PFD %u is too low (min %lu)\n",
+			name, val, MIN_PFD);
+	if (val > vco / 16)
+		pr_warn("%s: PFD %u is too high (max %u)\n",
+			name, val, vco / 16);
+
+	val = pll_readl(pll, PLL_CTRL1);
+
+	old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &
+		       PLL_INT_CTRL1_POSTDIV1_MASK;
+	old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &
+		       PLL_INT_CTRL1_POSTDIV2_MASK;
+	if (enabled &&
+	    (params->postdiv1 != old_postdiv1 ||
+	     params->postdiv2 != old_postdiv2))
+		pr_warn("%s: changing postdiv while PLL is enabled\n", name);
+
+	if (params->postdiv2 > params->postdiv1)
+		pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);
+
+	val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |
+		 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) |
+		 (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) |
+		 (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT));
+	val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |
+		(params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) |
+		(params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) |
+		(params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT);
+	pll_writel(pll, val, PLL_CTRL1);
+
+	if (enabled)
+		pll_lock(pll);
+
+	return 0;
+}
+
+static unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+	u32 val, prediv, fbdiv, postdiv1, postdiv2;
+	u64 rate = parent_rate;
+
+	val = pll_readl(pll, PLL_CTRL1);
+	prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;
+	fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;
+	postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &
+		PLL_INT_CTRL1_POSTDIV1_MASK;
+	postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &
+		PLL_INT_CTRL1_POSTDIV2_MASK;
+
+	rate *= fbdiv;
+	rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2);
+
+	return rate;
+}
+
+static struct clk_ops pll_gf40lp_laint_ops = {
+	.enable = pll_gf40lp_laint_enable,
+	.disable = pll_gf40lp_laint_disable,
+	.is_enabled = pll_gf40lp_laint_is_enabled,
+	.recalc_rate = pll_gf40lp_laint_recalc_rate,
+	.round_rate = pll_round_rate,
+	.set_rate = pll_gf40lp_laint_set_rate,
+};
+
+static struct clk_ops pll_gf40lp_laint_fixed_ops = {
+	.enable = pll_gf40lp_laint_enable,
+	.disable = pll_gf40lp_laint_disable,
+	.is_enabled = pll_gf40lp_laint_is_enabled,
+	.recalc_rate = pll_gf40lp_laint_recalc_rate,
+};
+
+static struct clk *pll_register(const char *name, const char *parent_name,
+				unsigned long flags, void __iomem *base,
+				enum pistachio_pll_type type,
+				struct pistachio_pll_rate_table *rates,
+				unsigned int nr_rates)
+{
+	struct pistachio_clk_pll *pll;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.flags = flags | CLK_GET_RATE_NOCACHE;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	switch (type) {
+	case PLL_GF40LP_FRAC:
+		if (rates)
+			init.ops = &pll_gf40lp_frac_ops;
+		else
+			init.ops = &pll_gf40lp_frac_fixed_ops;
+		break;
+	case PLL_GF40LP_LAINT:
+		if (rates)
+			init.ops = &pll_gf40lp_laint_ops;
+		else
+			init.ops = &pll_gf40lp_laint_fixed_ops;
+		break;
+	default:
+		pr_err("Unrecognized PLL type %u\n", type);
+		kfree(pll);
+		return ERR_PTR(-EINVAL);
+	}
+
+	pll->hw.init = &init;
+	pll->base = base;
+	pll->rates = rates;
+	pll->nr_rates = nr_rates;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
+
+void pistachio_clk_register_pll(struct pistachio_clk_provider *p,
+				struct pistachio_pll *pll,
+				unsigned int num)
+{
+	struct clk *clk;
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		clk = pll_register(pll[i].name, pll[i].parent,
+				   0, p->base + pll[i].reg_base,
+				   pll[i].type, pll[i].rates,
+				   pll[i].nr_rates);
+		p->clk_data.clks[pll[i].id] = clk;
+	}
+}
diff --git a/src/kernel/linux/v4.14/drivers/clk/pistachio/clk.c b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk.c
new file mode 100644
index 0000000..698cad4
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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.
+ */
+
+#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 "clk.h"
+
+struct pistachio_clk_provider *
+pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks)
+{
+	struct pistachio_clk_provider *p;
+
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return p;
+
+	p->clk_data.clks = kcalloc(num_clks, sizeof(struct clk *), GFP_KERNEL);
+	if (!p->clk_data.clks)
+		goto free_provider;
+	p->clk_data.clk_num = num_clks;
+	p->node = node;
+	p->base = of_iomap(node, 0);
+	if (!p->base) {
+		pr_err("Failed to map clock provider registers\n");
+		goto free_clks;
+	}
+
+	return p;
+
+free_clks:
+	kfree(p->clk_data.clks);
+free_provider:
+	kfree(p);
+	return NULL;
+}
+
+void pistachio_clk_register_provider(struct pistachio_clk_provider *p)
+{
+	unsigned int i;
+
+	for (i = 0; i < p->clk_data.clk_num; i++) {
+		if (IS_ERR(p->clk_data.clks[i]))
+			pr_warn("Failed to register clock %d: %ld\n", i,
+				PTR_ERR(p->clk_data.clks[i]));
+	}
+
+	of_clk_add_provider(p->node, of_clk_src_onecell_get, &p->clk_data);
+}
+
+void pistachio_clk_register_gate(struct pistachio_clk_provider *p,
+				 struct pistachio_gate *gate,
+				 unsigned int num)
+{
+	struct clk *clk;
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		clk = clk_register_gate(NULL, gate[i].name, gate[i].parent,
+					CLK_SET_RATE_PARENT,
+					p->base + gate[i].reg, gate[i].shift,
+					0, NULL);
+		p->clk_data.clks[gate[i].id] = clk;
+	}
+}
+
+void pistachio_clk_register_mux(struct pistachio_clk_provider *p,
+				struct pistachio_mux *mux,
+				unsigned int num)
+{
+	struct clk *clk;
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		clk = clk_register_mux(NULL, mux[i].name, mux[i].parents,
+				       mux[i].num_parents,
+				       CLK_SET_RATE_NO_REPARENT,
+				       p->base + mux[i].reg, mux[i].shift,
+				       get_count_order(mux[i].num_parents),
+				       0, NULL);
+		p->clk_data.clks[mux[i].id] = clk;
+	}
+}
+
+void pistachio_clk_register_div(struct pistachio_clk_provider *p,
+				struct pistachio_div *div,
+				unsigned int num)
+{
+	struct clk *clk;
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		clk = clk_register_divider(NULL, div[i].name, div[i].parent,
+					   0, p->base + div[i].reg, 0,
+					   div[i].width, div[i].div_flags,
+					   NULL);
+		p->clk_data.clks[div[i].id] = clk;
+	}
+}
+
+void pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p,
+					 struct pistachio_fixed_factor *ff,
+					 unsigned int num)
+{
+	struct clk *clk;
+	unsigned int i;
+
+	for (i = 0; i < num; i++) {
+		clk = clk_register_fixed_factor(NULL, ff[i].name, ff[i].parent,
+						0, 1, ff[i].div);
+		p->clk_data.clks[ff[i].id] = clk;
+	}
+}
+
+void pistachio_clk_force_enable(struct pistachio_clk_provider *p,
+				unsigned int *clk_ids, unsigned int num)
+{
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < num; i++) {
+		struct clk *clk = p->clk_data.clks[clk_ids[i]];
+
+		if (IS_ERR(clk))
+			continue;
+
+		err = clk_prepare_enable(clk);
+		if (err)
+			pr_err("Failed to enable clock %s: %d\n",
+			       __clk_get_name(clk), err);
+	}
+}
diff --git a/src/kernel/linux/v4.14/drivers/clk/pistachio/clk.h b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk.h
new file mode 100644
index 0000000..8d45178
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/clk/pistachio/clk.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __PISTACHIO_CLK_H
+#define __PISTACHIO_CLK_H
+
+#include <linux/clk-provider.h>
+
+struct pistachio_gate {
+	unsigned int id;
+	unsigned long reg;
+	unsigned int shift;
+	const char *name;
+	const char *parent;
+};
+
+#define GATE(_id, _name, _pname, _reg, _shift)	\
+	{					\
+		.id	= _id,			\
+		.reg	= _reg,			\
+		.shift	= _shift,		\
+		.name	= _name,		\
+		.parent = _pname,		\
+	}
+
+struct pistachio_mux {
+	unsigned int id;
+	unsigned long reg;
+	unsigned int shift;
+	unsigned int num_parents;
+	const char *name;
+	const char **parents;
+};
+
+#define PNAME(x) static const char *x[] __initconst
+
+#define MUX(_id, _name, _pnames, _reg, _shift)			\
+	{							\
+		.id		= _id,				\
+		.reg		= _reg,				\
+		.shift		= _shift,			\
+		.name		= _name,			\
+		.parents	= _pnames,			\
+		.num_parents	= ARRAY_SIZE(_pnames)		\
+	}
+
+
+struct pistachio_div {
+	unsigned int id;
+	unsigned long reg;
+	unsigned int width;
+	unsigned int div_flags;
+	const char *name;
+	const char *parent;
+};
+
+#define DIV(_id, _name, _pname, _reg, _width)			\
+	{							\
+		.id		= _id,				\
+		.reg		= _reg,				\
+		.width		= _width,			\
+		.div_flags	= 0,				\
+		.name		= _name,			\
+		.parent		= _pname,			\
+	}
+
+#define DIV_F(_id, _name, _pname, _reg, _width, _div_flags)	\
+	{							\
+		.id		= _id,				\
+		.reg		= _reg,				\
+		.width		= _width,			\
+		.div_flags	= _div_flags,			\
+		.name		= _name,			\
+		.parent		= _pname,			\
+	}
+
+struct pistachio_fixed_factor {
+	unsigned int id;
+	unsigned int div;
+	const char *name;
+	const char *parent;
+};
+
+#define FIXED_FACTOR(_id, _name, _pname, _div)			\
+	{							\
+		.id		= _id,				\
+		.div		= _div,				\
+		.name		= _name,			\
+		.parent		= _pname,			\
+	}
+
+struct pistachio_pll_rate_table {
+	unsigned long long fref;
+	unsigned long long fout;
+	unsigned long long refdiv;
+	unsigned long long fbdiv;
+	unsigned long long postdiv1;
+	unsigned long long postdiv2;
+	unsigned long long frac;
+};
+
+enum pistachio_pll_type {
+	PLL_GF40LP_LAINT,
+	PLL_GF40LP_FRAC,
+};
+
+struct pistachio_pll {
+	unsigned int id;
+	unsigned long reg_base;
+	enum pistachio_pll_type type;
+	struct pistachio_pll_rate_table *rates;
+	unsigned int nr_rates;
+	const char *name;
+	const char *parent;
+};
+
+#define PLL(_id, _name, _pname, _type, _reg, _rates)		\
+	{							\
+		.id		= _id,				\
+		.reg_base	= _reg,				\
+		.type		= _type,			\
+		.rates		= _rates,			\
+		.nr_rates	= ARRAY_SIZE(_rates),		\
+		.name		= _name,			\
+		.parent		= _pname,			\
+	}
+
+#define PLL_FIXED(_id, _name, _pname, _type, _reg)		\
+	{							\
+		.id		= _id,				\
+		.reg_base	= _reg,				\
+		.type		= _type,			\
+		.rates		= NULL,				\
+		.nr_rates	= 0,				\
+		.name		= _name,			\
+		.parent		= _pname,			\
+	}
+
+struct pistachio_clk_provider {
+	struct device_node *node;
+	void __iomem *base;
+	struct clk_onecell_data clk_data;
+};
+
+extern struct pistachio_clk_provider *
+pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks);
+extern void pistachio_clk_register_provider(struct pistachio_clk_provider *p);
+
+extern void pistachio_clk_register_gate(struct pistachio_clk_provider *p,
+					struct pistachio_gate *gate,
+					unsigned int num);
+extern void pistachio_clk_register_mux(struct pistachio_clk_provider *p,
+				       struct pistachio_mux *mux,
+				       unsigned int num);
+extern void pistachio_clk_register_div(struct pistachio_clk_provider *p,
+				       struct pistachio_div *div,
+				       unsigned int num);
+extern void
+pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p,
+				    struct pistachio_fixed_factor *ff,
+				    unsigned int num);
+extern void pistachio_clk_register_pll(struct pistachio_clk_provider *p,
+				       struct pistachio_pll *pll,
+				       unsigned int num);
+
+extern void pistachio_clk_force_enable(struct pistachio_clk_provider *p,
+				       unsigned int *clk_ids, unsigned int num);
+
+#endif