lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <1553350115-2614-1-git-send-email-qiaozhou@asrmicro.com>
Date:   Sat, 23 Mar 2019 22:08:35 +0800
From:   qiaozhou <qiaozhou@...micro.com>
To:     Michael Turquette <mturquette@...libre.com>,
        Stephen Boyd <sboyd@...nel.org>,
        Dan Carpenter <dan.carpenter@...cle.com>,
        <linux-kernel@...r.kernel.org>, <linux-clk@...r.kernel.org>
CC:     Qiao Zhou <qiaozhou@...micro.com>
Subject: [PATCH] clk: asr: clock driver support for ASR AquilaC Soc

From: Qiao Zhou <qiaozhou@...micro.com>

add clock driver support for ASR AquilaC SoC.

We add clk-gate, clk-mix, and clk-pll drivers:
1. clk-gate driver is for regisers which have different enable/disable bits
to control gating.
2. clk-mix driver is for registers which request to set div and mux
bits at the same time.
3. clk-pll driver is for pll configuration.

Signed-off-by: qiaozhou <qiaozhou@...micro.com>
---
 drivers/clk/Kconfig           |   1 +
 drivers/clk/Makefile          |   1 +
 drivers/clk/asr/Kconfig       |  17 ++
 drivers/clk/asr/Makefile      |   6 +
 drivers/clk/asr/clk-aquilac.c | 595 ++++++++++++++++++++++++++++++++++++++
 drivers/clk/asr/clk-gate.c    | 151 ++++++++++
 drivers/clk/asr/clk-mix.c     | 393 ++++++++++++++++++++++++++
 drivers/clk/asr/clk-pll.c     | 642 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/asr/clk-pll.h     |  91 ++++++
 drivers/clk/asr/clk.c         | 222 +++++++++++++++
 drivers/clk/asr/clk.h         | 235 ++++++++++++++++
 11 files changed, 2354 insertions(+)
 create mode 100644 drivers/clk/asr/Kconfig
 create mode 100644 drivers/clk/asr/Makefile
 create mode 100644 drivers/clk/asr/clk-aquilac.c
 create mode 100644 drivers/clk/asr/clk-gate.c
 create mode 100644 drivers/clk/asr/clk-mix.c
 create mode 100644 drivers/clk/asr/clk-pll.c
 create mode 100644 drivers/clk/asr/clk-pll.h
 create mode 100644 drivers/clk/asr/clk.c
 create mode 100644 drivers/clk/asr/clk.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index e705aab..0ea9f74 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -315,5 +315,6 @@ source "drivers/clk/tegra/Kconfig"
 source "drivers/clk/ti/Kconfig"
 source "drivers/clk/uniphier/Kconfig"
 source "drivers/clk/zynqmp/Kconfig"
+source "drivers/clk/asr/Kconfig"
 
 endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 1db1336..a88b3b9 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -112,3 +112,4 @@ endif
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
 obj-$(CONFIG_COMMON_CLK_ZYNQMP)         += zynqmp/
+obj-$(CONFIG_COMMON_CLK_ASR)		+= asr/
diff --git a/drivers/clk/asr/Kconfig b/drivers/clk/asr/Kconfig
new file mode 100644
index 0000000..ded68b5
--- /dev/null
+++ b/drivers/clk/asr/Kconfig
@@ -0,0 +1,17 @@
+config COMMON_CLK_ASR
+	bool "Support for ASR's clock controllers"
+	depends on OF
+	depends on ARCH_ASR || COMPILE_TEST
+	help
+	  Build the clock drivers for ASR8751C SoC. It contains clock-gate,
+	  clock-mix, clock-pll drivers which manages general gating, divider,
+	  mux, and pll enabling/disabling controls.
+	  Say Y if you want to support clock controls on ASR8751C SoCs
+
+config ASR_AQUILAC_CLK
+	bool "ASR AquilaC clock controller support"
+	depends on OF
+	depends on ARCH_ASR
+	depends on COMMON_CLK_ASR
+	help
+	  Build the clock driver for ASR8751C AquilaC development board.
diff --git a/drivers/clk/asr/Makefile b/drivers/clk/asr/Makefile
new file mode 100644
index 0000000..f6b9bd3
--- /dev/null
+++ b/drivers/clk/asr/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for asr specific clk
+#
+
+obj-$(CONFIG_COMMON_CLK_ASR) += clk-pll.o clk-gate.o clk-mix.o clk.o
+obj-$(CONFIG_ASR_AQUILAC_CLK) += clk-aquilac.o
diff --git a/drivers/clk/asr/clk-aquilac.c b/drivers/clk/asr/clk-aquilac.c
new file mode 100644
index 0000000..7274b60
--- /dev/null
+++ b/drivers/clk/asr/clk-aquilac.c
@@ -0,0 +1,595 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * clock driver file for asr AquilaC
+ *
+ * Copyright (C) 2019 ASR Microelectronics(Shanghai) Co., Ltd.
+ * Gang Wu <gangwu@...micro.com>
+ * Qiao	Zhou <qiaozhou@...micro.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/devfreq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <dt-bindings/clock/asr8751c-clk.h>
+#include "clk.h"
+#include "clk-pll.h"
+
+/* APBC register offset for asr AquilaC */
+#define APBC_UART0			0x0
+#define APBC_UART1			0x4
+#define APBC_GPIO			0x8
+#define APBC_SSP0			0x1c
+#define APBC_IPC			0x24
+#define APBC_RTC			0x28
+#define APBC_TWSI0			0x2c
+#define APBC_KPC			0x30
+#define APBC_TIMERS			0x34
+#define APBC_AIB			0x3c
+#define APBC_SSP2			0x4c
+#define APBC_TWSI1			0x60
+#define APBC_THERMAL			0x6c
+#define APBC_UART2			0x78
+#define APBC_TWSI4			0x7c
+#define APBC_TWSI5			0x80
+#define APBC_TWSI6			0x84
+#define APBC_TWSI7			0x88
+#define APBC_TWSI8			0x8c
+
+#define MPMU_UART_PLL			0x14
+#define MPMU_WDTPCR			0x200
+
+#define APMU_SDH0			0x54
+#define APMU_SDH1			0x58
+#define APMU_SDH2			0xe0
+#define APMU_USB			0x5c
+#define APMU_DMA			0x64
+#define APMU_AES			0x68
+#define APMU_TRACE_CONFIG		0x108
+
+#define APB_SPARE_PLL1CR		0x100
+#define APB_SPARE_PLL2CR		0x104
+#define APB_SPARE_PLL3CR		0x108
+#define APB_SPARE_PLL4CR		0x124
+#define APB_SPARE_PLL5CR		0x114
+#define APB_SPARE_PLL6CR		0x13c
+#define APB_SPARE_PLL6CR2		0x140
+#define APB_SPARE_PLL7CR		0x148
+#define APB_SPARE_PLL7CR2		0x14c
+
+#define MPMU_PLL2CR			0x34
+#define MPMU_PLL3CR			0x1c
+#define MPMU_PLL4CR			0x50
+#define MPMU_PLL4CR			0x50
+#define MPMU_PLL5CR			0x4c
+#define MPMU_POSR			0x10
+#define POSR_PLL1_LOCK			(1 << 27)
+#define POSR_PLL2_LOCK			(1 << 28)
+#define POSR_PLL3_LOCK			(1 << 29)
+#define POSR_PLL4_LOCK			(1 << 30)
+#define POSR_PLL5_LOCK			(1 << 31)
+#define POSR_PLL6_LOCK			(1 << 31)
+#define POSR_PLL7_LOCK			(1 << 31)
+
+#define MPMU_ACGR			0x1024
+
+enum pll {
+	PLL2 = 0,
+	PLL3,
+	PLL4,
+	PLL5,
+	PLL6,
+	PLL7,
+	MAX_PLL_NUM,
+};
+
+struct plat_pll_info {
+	spinlock_t lock;
+	const char *vco_name;
+	/* clk flags */
+	unsigned long vco_flag;
+	unsigned long vcoclk_flag;
+	/* dt index */
+	unsigned int vcodtidx;
+};
+
+static struct asr_vco_params pllx_vco_params[MAX_PLL_NUM] __initdata = {
+	{
+		.vco_min = 600000000UL,
+		.vco_max = 2700000000UL,
+		.cr_off = MPMU_PLL2CR,
+		.swcr_off = APB_SPARE_PLL2CR,
+		.lock_off = MPMU_POSR,
+		.lock_enable_bit = POSR_PLL2_LOCK,
+	},
+	{
+		.vco_min = 600000000UL,
+		.vco_max = 2700000000UL,
+		.cr_off = MPMU_PLL3CR,
+		.swcr_off = APB_SPARE_PLL3CR,
+		.lock_off = MPMU_POSR,
+		.lock_enable_bit = POSR_PLL3_LOCK,
+	},
+	{
+		.vco_min = 600000000UL,
+		.vco_max = 2700000000UL,
+		.cr_off = MPMU_PLL4CR,
+		.swcr_off = APB_SPARE_PLL4CR,
+		.lock_off = MPMU_POSR,
+		.lock_enable_bit = POSR_PLL4_LOCK,
+	},
+	{
+		.vco_min = 600000000UL,
+		.vco_max = 2700000000UL,
+		.cr_off = MPMU_PLL5CR,
+		.swcr_off = APB_SPARE_PLL5CR,
+		.lock_off = MPMU_POSR,
+		.lock_enable_bit = POSR_PLL5_LOCK,
+	},
+	{
+		.vco_min = 600000000UL,
+		.vco_max = 2700000000UL,
+		.swcr_off = APB_SPARE_PLL6CR,
+		.swcr_off = APB_SPARE_PLL6CR2,
+		.lock_off = APB_SPARE_PLL6CR2,
+		.lock_enable_bit = POSR_PLL6_LOCK,
+	},
+	{
+		.vco_min = 600000000UL,
+		.vco_max = 2700000000UL,
+		.swcr_off = APB_SPARE_PLL7CR,
+		.swcr2_off = APB_SPARE_PLL7CR2,
+		.lock_off = APB_SPARE_PLL7CR2,
+		.lock_enable_bit = POSR_PLL7_LOCK,
+	},
+};
+
+static struct plat_pll_info pllx_platinfo[MAX_PLL_NUM] __initdata = {
+	{
+		.vco_name = "pll2_vco",
+		.vcoclk_flag = 0,
+		.vco_flag = ASR_PLL_FRAC_FEAT | ASR_PLL_SKIP_DEF_RATE,
+		.vcodtidx = ASR_CLK_PLL2_VCO,
+	},
+	{
+		.vco_name = "pll3_vco",
+		.vcoclk_flag = 0,
+		.vco_flag = ASR_PLL_FRAC_FEAT | ASR_PLL_SKIP_DEF_RATE,
+		.vcodtidx = ASR_CLK_PLL3_VCO,
+	},
+
+	{
+		.vco_name = "pll4_vco",
+		.vcoclk_flag = 0,
+		.vco_flag = ASR_PLL_FRAC_FEAT | ASR_PLL_SKIP_DEF_RATE,
+		.vcodtidx = ASR_CLK_PLL4_VCO,
+	},
+	{
+		.vco_name = "pll5_vco",
+		.vcoclk_flag = 0,
+		.vco_flag = ASR_PLL_FRAC_FEAT | ASR_PLL_SKIP_DEF_RATE,
+		.vcodtidx = ASR_CLK_PLL5_VCO,
+	},
+	{
+		.vco_name = "pll6_vco",
+		.vcoclk_flag = 0,
+		.vco_flag = ASR_PLL_FRAC_FEAT | ASR_PLL_REG_NEW | ASR_PLL_SKIP_DEF_RATE,
+		.vcodtidx = ASR_CLK_PLL6_VCO,
+	},
+	{
+		.vco_name = "pll7_vco",
+		.vcoclk_flag = 0,
+		.vco_flag = ASR_PLL_FRAC_FEAT | ASR_PLL_FRAC_AUDIO | ASR_PLL_REG_NEW | ASR_PLL_SKIP_DEF_RATE,
+		.vcodtidx = ASR_CLK_PLL7_VCO,
+	},
+};
+
+static DEFINE_SPINLOCK(pll1_lock);
+static DEFINE_SPINLOCK(pll2_lock);
+static DEFINE_SPINLOCK(pll3_lock);
+static DEFINE_SPINLOCK(pll4_lock);
+static DEFINE_SPINLOCK(pll5_lock);
+static DEFINE_SPINLOCK(pll6_lock);
+static DEFINE_SPINLOCK(pll7_lock);
+static DEFINE_SPINLOCK(pll1_lock_2);
+static DEFINE_SPINLOCK(uart0_lock);
+static DEFINE_SPINLOCK(uart1_lock);
+static DEFINE_SPINLOCK(uart2_lock);
+static DEFINE_SPINLOCK(timer0_lock);
+static DEFINE_SPINLOCK(sdh0_lock);
+static DEFINE_SPINLOCK(sdh1_lock);
+static DEFINE_SPINLOCK(sdh2_lock);
+
+static const char * const uart_parent[] = {"pll1_58p5", "uart_pll"};
+static const char * const aes_parent[] = {"pll1_208", "pll1_104"};
+static const char * const ssp_parent[] = {
+	"pll1_6p5", "pll1_13", "pll1_26", "pll1_52"};
+static const char * const timer_parent[] = {
+	"pll1_13", "clk32", "pll1_6p5", "vctcxo_3p25", "vctcxo_1"};
+static const char * const sdh_parent_names[] = {
+	"pll1_416", "pll1_624", "pll6_d2", "pll6_d4"};
+static const char * const keep_on_clocks_tbl[] = {
+	"dma_clk", "gpio_clk", "pll1_624", "pll1_26"};
+static unsigned long pll_rates[MAX_PLL_NUM] = {
+	2100 * MHZ, 1900 * MHZ, 1400 * MHZ, 1540 * MHZ,
+	1600 * MHZ, 1536 * MHZ};
+
+static struct asr_clk_mix_clk_table sdh_pptbl[] = {
+	{.rate = 200000000, .parent_index = 3, },
+	{.rate = 208000000, .parent_index = 0, },
+	{.rate = 312000000, .parent_index = 1, },
+	{.rate = 400000000, .parent_index = 3, },
+	{.rate = 800000000, .parent_index = 2, },
+};
+static struct asr_clk_mix_config sdh_mix_config = {
+	.reg_info = DEFINE_MIX_REG_INFO(3, 8, 2, 6, (0x1 << 11)),
+	.table = sdh_pptbl,
+	.table_size = ARRAY_SIZE(sdh_pptbl),
+};
+
+static struct asr_param_mux_clk apbc_mux_clks[] __initdata = {
+	{ASR_CLK_MUX_TIMER0,	"timer0_mux",	timer_parent,	ARRAY_SIZE(timer_parent),	CLK_SET_RATE_PARENT,	APBC_TIMERS,	4, 3, 0, &timer0_lock},
+	{ASR_CLK_MUX_UART0,	"uart0_mux",	uart_parent,	ARRAY_SIZE(uart_parent),	CLK_SET_RATE_PARENT,	APBC_UART0,	4, 3, 0, &uart0_lock},
+	{ASR_CLK_MUX_UART1,	"uart1_mux",	uart_parent,	ARRAY_SIZE(uart_parent),	CLK_SET_RATE_PARENT,	APBC_UART1,	4, 3, 0, &uart1_lock},
+	{ASR_CLK_MUX_UART2,	"uart2_mux",	uart_parent,	ARRAY_SIZE(uart_parent),	CLK_SET_RATE_PARENT,	APBC_UART2,	4, 3, 0, &uart2_lock},
+	{ASR_CLK_MUX_SSP0,	"ssp0_mux",	ssp_parent,	ARRAY_SIZE(ssp_parent),		CLK_SET_RATE_PARENT,	APBC_SSP0,	7, 3, 0, NULL},
+	{ASR_CLK_MUX_SSP2,	"ssp2_mux",	ssp_parent,	ARRAY_SIZE(ssp_parent),		CLK_SET_RATE_PARENT,	APBC_SSP2,	7, 3, 0, NULL},
+};
+
+static struct asr_param_mux_clk apmu_mux_clks[] __initdata = {
+	{ASR_CLK_MUX_AES,	"aes_mux",	aes_parent,	ARRAY_SIZE(aes_parent),		CLK_SET_RATE_PARENT,	APMU_AES,	6, 1, 0, NULL},
+};
+
+static struct asr_param_fixed_rate_clk fixed_rate_clks[] = {
+	{ASR_CLK_CLK32,		"clk32",		NULL,	0,	32768},
+	{ASR_CLK_VCTCXO,	"vctcxo",		NULL,	0,	26000000},
+	{ASR_CLK_VCTCXO_3P25M,	"vctcxo_3p25",		NULL,	0,	3250000},
+	{ASR_CLK_VCTCXO_1M,	"vctcxo_1",		NULL,	0,	1000000},
+	{ASR_CLK_PLL1_VCO,	"pll1_2496_vco",	NULL,	0,	2496000000},
+};
+
+/* general gate clk controlled by APB_SPARE based registers */
+static struct asr_param_general_gate_clk general_gate_clks[] __initdata = {
+	{ASR_CLK_PLL1_D1_2496,	"pll1_d1_2496",	"pll1_d1_2496_vco",	APB_SPARE_PLL1CR, 26, 0, &pll1_lock, 0},
+	{ASR_CLK_PLL1_D2_1248,	"pll1_d2_1248",	"pll1_d2_1248_vco",	APB_SPARE_PLL1CR, 27, 0, &pll1_lock, 0},
+	{ASR_CLK_PLL1_D3_832,	"pll1_d3_832",	"pll1_d3_832_vco",	APB_SPARE_PLL1CR, 28, 0, &pll1_lock, 0},
+	{ASR_CLK_PLL1_D4_624,	"pll1_d4_624",	"pll1_d4_624_vco",	APB_SPARE_PLL1CR, 29, 0, &pll1_lock, 0},
+	{ASR_CLK_PLL1_D5_499,	"pll1_d5_499",	"pll1_d5_499_vco",	APB_SPARE_PLL1CR, 30, 0, &pll1_lock, 0},
+
+	{ASR_CLK_PLL2_D1,	"pll2_d1",	"pll2_d1_vco",		APB_SPARE_PLL2CR, 26, 0, &pll2_lock},
+	{ASR_CLK_PLL2_D2,	"pll2_d2",	"pll2_d2_vco",		APB_SPARE_PLL2CR, 27, 0, &pll2_lock},
+	{ASR_CLK_PLL2_D3,	"pll2_d3",	"pll2_d3_vco",		APB_SPARE_PLL2CR, 28, 0, &pll2_lock},
+	{ASR_CLK_PLL2_D4,	"pll2_d4",	"pll2_d4_vco",		APB_SPARE_PLL2CR, 29, 0, &pll2_lock},
+	{ASR_CLK_PLL2_D5,	"pll2_d5",	"pll2_d5_vco",		APB_SPARE_PLL2CR, 30, 0, &pll2_lock},
+
+	{ASR_CLK_PLL3_D1,	"pll3_d1",	"pll3_d1_vco",		APB_SPARE_PLL3CR, 26, 0, &pll3_lock, CLK_SET_RATE_PARENT},
+	{ASR_CLK_PLL3_D2,	"pll3_d2",	"pll3_d2_vco",		APB_SPARE_PLL3CR, 27, 0, &pll3_lock, 0},
+	{ASR_CLK_PLL3_D3,	"pll3_d3",	"pll3_d3_vco",		APB_SPARE_PLL3CR, 28, 0, &pll3_lock, 0},
+	{ASR_CLK_PLL3_D4,	"pll3_d4",	"pll3_d4_vco",		APB_SPARE_PLL3CR, 29, 0, &pll3_lock, 0},
+	{ASR_CLK_PLL3_D5,	"pll3_d5",	"pll3_d5_vco",		APB_SPARE_PLL3CR, 30, 0, &pll3_lock, 0},
+	{ASR_CLK_PLL4_D1,	"pll4_d1",	"pll4_d1_vco",		APB_SPARE_PLL4CR, 26, 0, &pll4_lock, CLK_SET_RATE_PARENT},
+	{ASR_CLK_PLL4_D2,	"pll4_d2",	"pll4_d2_vco",		APB_SPARE_PLL4CR, 27, 0, &pll4_lock, 0},
+	{ASR_CLK_PLL4_D3,	"pll4_d3",	"pll4_d3_vco",		APB_SPARE_PLL4CR, 28, 0, &pll4_lock, 0},
+	{ASR_CLK_PLL4_D4,	"pll4_d4",	"pll4_d4_vco",		APB_SPARE_PLL4CR, 29, 0, &pll4_lock, 0},
+	{ASR_CLK_PLL4_D5,	"pll4_d5",	"pll4_d5_vco",		APB_SPARE_PLL4CR, 30, 0, &pll4_lock, 0},
+
+	{ASR_CLK_PLL5_D1,	"pll5_d1",	"pll5_d1_vco",		APB_SPARE_PLL5CR, 26, 0, &pll5_lock, CLK_SET_RATE_PARENT},
+	{ASR_CLK_PLL5_D2,	"pll5_d2",	"pll5_d2_vco",		APB_SPARE_PLL5CR, 27, 0, &pll5_lock},
+	{ASR_CLK_PLL5_D3,	"pll5_d3",	"pll5_d3_vco",		APB_SPARE_PLL5CR, 28, 0, &pll5_lock},
+	{ASR_CLK_PLL5_D4,	"pll5_d4",	"pll5_d4_vco",		APB_SPARE_PLL5CR, 29, 0, &pll5_lock},
+	{ASR_CLK_PLL5_D5,	"pll5_d5",	"pll5_d5_vco",		APB_SPARE_PLL5CR, 30, 0, &pll5_lock},
+
+	{ASR_CLK_PLL6_D1,	"pll6_d1",	"pll6_d1_vco",		APB_SPARE_PLL6CR, 26, 0, &pll6_lock},
+	{ASR_CLK_PLL6_D2,	"pll6_d2",	"pll6_d2_vco",		APB_SPARE_PLL6CR, 27, 0, &pll6_lock},
+	{ASR_CLK_PLL6_D3,	"pll6_d3",	"pll6_d3_vco",		APB_SPARE_PLL6CR, 28, 0, &pll6_lock},
+	{ASR_CLK_PLL6_D4,	"pll6_d4",	"pll6_d4_vco",		APB_SPARE_PLL6CR, 29, 0, &pll6_lock},
+	{ASR_CLK_PLL6_D5,	"pll6_d5",	"pll6_d5_vco",		APB_SPARE_PLL6CR, 30, 0, &pll6_lock},
+
+	{ASR_CLK_PLL7_D1,	"pll7_d1",	"pll7_d1_vco",		APB_SPARE_PLL7CR, 26, 0, &pll7_lock},
+	{ASR_CLK_PLL7_D2,	"pll7_d2",	"pll7_d2_vco",		APB_SPARE_PLL7CR, 27, 0, &pll7_lock},
+	{ASR_CLK_PLL7_D3,	"pll7_d3",	"pll7_d3_vco",		APB_SPARE_PLL7CR, 28, 0, &pll7_lock},
+	{ASR_CLK_PLL7_D4,	"pll7_d4",	"pll7_d4_vco",		APB_SPARE_PLL7CR, 29, 0, &pll7_lock},
+	{ASR_CLK_PLL7_D5,	"pll7_d5",	"pll7_d5_vco",		APB_SPARE_PLL7CR, 30, 0, &pll7_lock},
+};
+
+/* general gate clk controlld by MPMU_ACGR based registers */
+static struct asr_param_general_gate_clk general_gate_clks_2[] __initdata = {
+	{ASR_CLK_PLL1_499,	"pll1_499",	"pll1_d5_499",		MPMU_ACGR, 21,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_13_WDT,	"pll1_13_wdt",	"pll1_d192_13",		MPMU_ACGR, 19,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_249,	"pll1_249",	"pll1_d10_249",		MPMU_ACGR, 18,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_1248,	"pll1_1248",	"pll1_d2_1248",		MPMU_ACGR, 16,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_624,	"pll1_624",	"pll1_d4_624",		MPMU_ACGR, 15,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_832,	"pll1_832",	"pll1_d3_832",		MPMU_ACGR, 14,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_312,	"pll1_312",	"pll1_d8_312",		MPMU_ACGR, 13,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_104,	"pll1_104",	"pll1_d24_104",		MPMU_ACGR, 12,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_52,	"pll1_52",	"pll1_d48_52",		MPMU_ACGR, 11,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_48,	"pll1_48",	"pll1_d52_48",		MPMU_ACGR, 10,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_58P5,	"pll1_58p5",	"pll1_m3d128_58p5",	MPMU_ACGR, 8,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_52_2,	"pll1_52_2",	"pll1_d48_52",		MPMU_ACGR, 7,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_32,	"pll1_32",	"pll1_d78_32",		MPMU_ACGR, 6,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_208,	"pll1_208",	"pll1_d12_208",		MPMU_ACGR, 5,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_26,	"pll1_26",	"pll1_d96_26",		MPMU_ACGR, 4,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_13,	"pll1_13",	"pll1_d192_13",		MPMU_ACGR, 3,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_6P5,	"pll1_6p5",	"pll1_d384_6p5",	MPMU_ACGR, 2,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_78_UART,	"pll1_uart",	"pll1_d32_78",		MPMU_ACGR, 1,	0, &pll1_lock_2, 0},
+	{ASR_CLK_PLL1_416,	"pll1_416",	"pll1_d6_416",		MPMU_ACGR, 0,	0, &pll1_lock_2, 0},
+};
+
+/* gate clk: enable/disable/reset bits are different, APB based. */
+static struct asr_param_gate_clk apbc_gate_clks[] __initdata = {
+	{ASR_CLK_TWSI0,		"twsi0_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI0,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_GPIO,		"gpio_clk",	"vctcxo",	CLK_SET_RATE_PARENT,	APBC_GPIO,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_IPC,		"ipc_clk",	NULL,		0,			APBC_IPC,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_TIMER0,	"timer0_clk",	"timer0_mux",	CLK_SET_RATE_PARENT,	APBC_TIMERS,	0x7,	0x3,	0x0, 0,				&timer0_lock},
+	{ASR_CLK_AIB,		"aib_clk",	"vctcxo",	CLK_SET_RATE_PARENT,	APBC_AIB,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_UART0,		"uart0_clk",	"uart0_mux",	CLK_SET_RATE_PARENT,	APBC_UART0,	0x7,	0x3,	0x0, 0,				&uart0_lock},
+	{ASR_CLK_UART1,		"uart1_clk",	"uart1_mux",	CLK_SET_RATE_PARENT,	APBC_UART1,	0x7,	0x3,	0x0, 0,				&uart1_lock},
+	{ASR_CLK_UART2,		"uart2_clk",	"uart2_mux",	CLK_SET_RATE_PARENT,	APBC_UART2,	0x7,	0x3,	0x0, 0,				&uart2_lock},
+	{ASR_CLK_KPC,		"kpc_clk",	"clk32",	CLK_SET_RATE_PARENT,	APBC_KPC,	0x7,	0x3,	0x0, ASR_CLK_GATE_NEED_DELAY,	NULL},
+	{ASR_CLK_RTC,		"rtc_clk",	"clk32",	CLK_SET_RATE_PARENT,	APBC_RTC,	0x87,	0x83,	0x0, ASR_CLK_GATE_NEED_DELAY,	NULL},
+	{ASR_CLK_TWSI1,		"twsi1_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI1,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_THERMAL,	"thermal_clk",	NULL,		0,			APBC_THERMAL,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_TWSI4,		"twsi4_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI4,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_TWSI5,		"twsi5_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI5,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_TWSI6,		"twsi6_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI6,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_TWSI7,		"twsi7_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI7,	0x7,	0x3,	0x0, 0,				NULL},
+	{ASR_CLK_TWSI8,		"twsi8_clk",	"pll1_32",	CLK_SET_RATE_PARENT,	APBC_TWSI8,	0x7,	0x3,	0x0, 0,				NULL},
+};
+
+/* gate clk: enable/disable/reset bits are different, APMU based. */
+static struct asr_param_gate_clk apmu_gate_clks[] __initdata = {
+	{ASR_CLK_DMA,		"dma_clk",	NULL,		0,	APMU_DMA,	0x9,	0x9,	0x0, 0,	NULL},
+	{ASR_CLK_USB,		"usb_clk",	NULL,		0,	APMU_USB,	0x3,	0x3,	0x0, 0,	NULL},
+	{ASR_CLK_SDH_AXI,	"sdh_axi_clk",	NULL,		0,	APMU_SDH0,	0x9,	0x9,	0x0, 0,	NULL},
+	{ASR_CLK_SDH0,		"sdh0_clk",	"sdh0_mix_clk",	0,	APMU_SDH0,	0x12,	0x12,	0x0, 0,	 &sdh0_lock},
+	{ASR_CLK_SDH1,		"sdh1_clk",	"sdh0_mix_clk",	0,	APMU_SDH1,	0x12,	0x12,	0x0, 0,	 &sdh1_lock},
+	{ASR_CLK_SDH2,		"sdh2_clk",	"sdh0_mix_clk",	0,	APMU_SDH2,	0x12,	0x12,	0x0, 0,	 &sdh2_lock},
+	{ASR_CLK_AES,		"aes_clk",	"aes_mux",	0,	APMU_AES,	0x30,	0x30,	0x0, 0,	NULL},
+};
+
+/* gate clk: enable/disable/reset bits are different, MPMU based. */
+static struct asr_param_gate_clk mpmu_gate_clks[] __initdata = {
+	{ASR_CLK_WDT,	"wdt_clk",	"pll1_26",	0,	MPMU_WDTPCR,	0x7,	0x3,	0x4, 0,	NULL},
+};
+
+/* fixed factor clk */
+static struct asr_param_fixed_factor_clk fixed_factor_clks[] __initdata = {
+	{ASR_CLK_PLL1_D1_2496_VCO,	"pll1_d1_2496_vco",	"pll1_2496_vco",	1, 1, 0},
+	{ASR_CLK_PLL1_D2_1248_VCO,	"pll1_d2_1248_vco",	"pll1_2496_vco",	1, 2, 0},
+	{ASR_CLK_PLL1_D3_832_VCO,	"pll1_d3_832_vco",	"pll1_2496_vco",	1, 3, 0},
+	{ASR_CLK_PLL1_D4_624_VCO,	"pll1_d4_624_vco",	"pll1_2496_vco",	1, 4, 0},
+	{ASR_CLK_PLL1_D5_499_VCO,	"pll1_d5_499_vco",	"pll1_2496_vco",	1, 5, 0},
+	{ASR_CLK_PLL1_D6_416,		"pll1_d6_416",		"pll1_d3_832",		1, 2, 0},
+	{ASR_CLK_PLL1_D8_312,		"pll1_d8_312",		"pll1_d4_624",		1, 2, 0},
+	{ASR_CLK_PLL1_D10_249,		"pll1_d10_249",		"pll1_d5_499",		1, 2, 0},
+	{ASR_CLK_PLL1_D12_208,		"pll1_d12_208",		"pll1_d3_832",		1, 4, 0},
+	{ASR_CLK_PLL1_D24_104,		"pll1_d24_104",		"pll1_d4_624",		1, 6, 0},
+	{ASR_CLK_PLL1_D32_78,		"pll1_d32_78",		"pll1_d4_624",		1, 8, 0},
+	{ASR_CLK_PLL1_D32_78_2,		"pll1_78",		"pll1_312",		1, 4, 0},
+	{ASR_CLK_PLL1_D48_52,		"pll1_d48_52",		"pll1_d4_624",		1, 12, 0},
+	{ASR_CLK_PLL1_D52_48,		"pll1_d52_48",		"pll1_d4_624",		1, 13, 0},
+	{ASR_CLK_PLL1_M3D128_58P5,	"pll1_m3d128_58p5",	"pll1_d4_624",		3, 32, 0},
+	{ASR_CLK_PLL1_D78_32,		"pll1_d78_32",		"pll1_d4_624",		2, 39, 0},
+	{ASR_CLK_PLL1_D96_26,		"pll1_d96_26",		"pll1_d4_624",		1, 24, 0},
+	{ASR_CLK_PLL1_D192_13,		"pll1_d192_13",		"pll1_d4_624",		1, 48, 0},
+	{ASR_CLK_PLL1_D384_6P5,		"pll1_d384_6p5",	"pll1_d4_624",		1, 96, 0},
+
+	{ASR_CLK_PLL2_D1_VCO,		"pll2_d1_vco",		"pll2_vco",		1, 1, 0},
+	{ASR_CLK_PLL2_D2_VCO,		"pll2_d2_vco",		"pll2_vco",		1, 2, 0},
+	{ASR_CLK_PLL2_D3_VCO,		"pll2_d3_vco",		"pll2_vco",		1, 3, 0},
+	{ASR_CLK_PLL2_D4_VCO,		"pll2_d4_vco",		"pll2_vco",		1, 4, 0},
+	{ASR_CLK_PLL2_D5_VCO,		"pll2_d5_vco",		"pll2_vco",		1, 5, 0},
+
+	{ASR_CLK_PLL3_D1_VCO,		"pll3_d1_vco",		"pll3_vco",		1, 1, CLK_SET_RATE_PARENT},
+	{ASR_CLK_PLL3_D2_VCO,		"pll3_d2_vco",		"pll3_vco",		1, 2, 0},
+	{ASR_CLK_PLL3_D3_VCO,		"pll3_d3_vco",		"pll3_vco",		1, 3, 0},
+	{ASR_CLK_PLL3_D4_VCO,		"pll3_d4_vco",		"pll3_vco",		1, 4, 0},
+	{ASR_CLK_PLL3_D5_VCO,		"pll3_d5_vco",		"pll3_vco",		1, 5, 0},
+
+	{ASR_CLK_PLL4_D1_VCO,		"pll4_d1_vco",		"pll4_vco",		1, 1, CLK_SET_RATE_PARENT},
+	{ASR_CLK_PLL4_D2_VCO,		"pll4_d2_vco",		"pll4_vco",		1, 2, 0},
+	{ASR_CLK_PLL4_D3_VCO,		"pll4_d3_vco",		"pll4_vco",		1, 3, 0},
+	{ASR_CLK_PLL4_D4_VCO,		"pll4_d4_vco",		"pll4_vco",		1, 4, 0},
+	{ASR_CLK_PLL4_D5_VCO,		"pll4_d5_vco",		"pll4_vco",		1, 5, 0},
+
+	{ASR_CLK_PLL5_D1_VCO,		"pll5_d1_vco",		"pll5_vco",		1, 1, CLK_SET_RATE_PARENT},
+	{ASR_CLK_PLL5_D2_VCO,		"pll5_d2_vco",		"pll5_vco",		1, 2, 0},
+	{ASR_CLK_PLL5_D3_VCO,		"pll5_d3_vco",		"pll5_vco",		1, 3, 0},
+	{ASR_CLK_PLL5_D4_VCO,		"pll5_d4_vco",		"pll5_vco",		1, 4, 0},
+	{ASR_CLK_PLL5_D5_VCO,		"pll5_d5_vco",		"pll5_vco",		1, 5, 0},
+
+	{ASR_CLK_PLL6_D1_VCO,		"pll6_d1_vco",		"pll6_vco",		1, 1, 0},
+	{ASR_CLK_PLL6_D2_VCO,		"pll6_d2_vco",		"pll6_vco",		1, 2, 0},
+	{ASR_CLK_PLL6_D3_VCO,		"pll6_d3_vco",		"pll6_vco",		1, 3, 0},
+	{ASR_CLK_PLL6_D4_VCO,		"pll6_d4_vco",		"pll6_vco",		1, 4, 0},
+	{ASR_CLK_PLL6_D5_VCO,		"pll6_d5_vco",		"pll6_vco",		1, 5, 0},
+
+	{ASR_CLK_PLL7_D1_VCO,		"pll7_d1_vco",		"pll7_vco",		1, 1, 0},
+	{ASR_CLK_PLL7_D2_VCO,		"pll7_d2_vco",		"pll7_vco",		1, 2, 0},
+	{ASR_CLK_PLL7_D3_VCO,		"pll7_d3_vco",		"pll7_vco",		1, 3, 0},
+	{ASR_CLK_PLL7_D4_VCO,		"pll7_d4_vco",		"pll7_vco",		1, 4, 0},
+	{ASR_CLK_PLL7_D5_VCO,		"pll7_d5_vco",		"pll7_vco",		1, 5, 0},
+};
+
+static void __init aquilac_general_clk_init(struct asr_clk_data *clock_data)
+{
+	struct clk *clk;
+	struct asr_clk_unit *unit = &clock_data->unit;
+
+	asr_register_fixed_rate_clks(unit, fixed_rate_clks,
+		ARRAY_SIZE(fixed_rate_clks));
+
+	clk = clk_register_fractional_divider(NULL, "uart_pll", "pll1_uart",
+		CLK_SET_RATE_PARENT,
+		clock_data->mpmu_base + MPMU_UART_PLL,
+		0, 13, 16, 13, 0, NULL);
+	asr_clk_add(unit, ASR_CLK_PLL1_78_UART, clk);
+
+	asr_register_general_gate_clks(unit, general_gate_clks,
+		clock_data->apbs_base, ARRAY_SIZE(general_gate_clks));
+
+	asr_register_general_gate_clks(unit, general_gate_clks_2,
+		clock_data->mpmu_base, ARRAY_SIZE(general_gate_clks_2));
+
+	asr_register_fixed_factor_clks(unit, fixed_factor_clks,
+		ARRAY_SIZE(fixed_factor_clks));
+}
+
+static void __init aquilac_pll_init(struct asr_clk_data *clock_data)
+{
+	struct clk *clk;
+	struct asr_clk_unit *unit = &clock_data->unit;
+	int idx;
+
+	for (idx = 0; idx < MAX_PLL_NUM; idx++) {
+		spin_lock_init(&pllx_platinfo[idx].lock);
+
+		pllx_vco_params[idx].pll_swcr = clock_data->apbs_base + pllx_vco_params[idx].swcr_off;
+		/* pll6/7 have different base of some regs */
+		if (idx < PLL6) {
+			pllx_vco_params[idx].cr_reg = clock_data->mpmu_base + pllx_vco_params[idx].cr_off;
+			pllx_vco_params[idx].lock_reg = clock_data->mpmu_base + pllx_vco_params[idx].lock_off;
+		} else {
+			pllx_vco_params[idx].pll_swcr2 = clock_data->apbs_base + pllx_vco_params[idx].swcr2_off;
+			pllx_vco_params[idx].lock_reg = clock_data->apbs_base + pllx_vco_params[idx].lock_off;
+		}
+
+		pllx_vco_params[idx].default_rate = pll_rates[idx];
+
+		clk = asr_clk_register_vco(pllx_platinfo[idx].vco_name,
+			0, pllx_platinfo[idx].vcoclk_flag, pllx_platinfo[idx].vco_flag,
+			&pllx_platinfo[idx].lock, &pllx_vco_params[idx]);
+		if (!__clk_is_enabled(clk))
+			clk_set_rate(clk, pllx_vco_params[idx].default_rate);
+		asr_clk_add(unit, pllx_platinfo[idx].vcodtidx, clk);
+	}
+}
+static void __init aquilac_periph_clk_init(struct asr_clk_data *clock_data)
+{
+	struct asr_clk_unit *unit = &clock_data->unit;
+
+	asr_register_mux_clks(unit, apbc_mux_clks, clock_data->apbc_base,
+		ARRAY_SIZE(apbc_mux_clks));
+
+	asr_register_mux_clks(unit, apmu_mux_clks, clock_data->apmu_base,
+		ARRAY_SIZE(apmu_mux_clks));
+
+	asr_register_gate_clks(unit, apbc_gate_clks, clock_data->apbc_base,
+		ARRAY_SIZE(apbc_gate_clks));
+
+	asr_register_gate_clks(unit, apmu_gate_clks, clock_data->apmu_base,
+		ARRAY_SIZE(apmu_gate_clks));
+
+	asr_register_gate_clks(unit, mpmu_gate_clks, clock_data->mpmu_base,
+		ARRAY_SIZE(mpmu_gate_clks));
+}
+
+static void __init aquilac_mix_clk_init(struct asr_clk_data *clock_data)
+{
+	sdh_mix_config.reg_info.reg_clk_ctrl = clock_data->apmu_base + APMU_SDH0;
+	asr_clk_register_mix(NULL, "sdh0_mix_clk",
+		(const char **)sdh_parent_names, ARRAY_SIZE(sdh_parent_names),
+		CLK_SET_RATE_PARENT,
+		&sdh_mix_config, &sdh0_lock);
+
+	sdh_mix_config.reg_info.reg_clk_ctrl = clock_data->apmu_base + APMU_SDH1;
+	asr_clk_register_mix(NULL, "sdh1_mix_clk",
+		(const char **)sdh_parent_names, ARRAY_SIZE(sdh_parent_names),
+		CLK_SET_RATE_PARENT,
+		&sdh_mix_config, &sdh1_lock);
+
+	sdh_mix_config.reg_info.reg_clk_ctrl = clock_data->apmu_base + APMU_SDH2;
+	asr_clk_register_mix(NULL, "sdh2_mix_clk",
+		(const char **)sdh_parent_names, ARRAY_SIZE(sdh_parent_names),
+		CLK_SET_RATE_PARENT,
+		&sdh_mix_config, &sdh2_lock);
+}
+
+static int __init aquilac_clk_of_iomap(struct device_node *np, struct asr_clk_data *acu)
+{
+	acu->mpmu_base = of_iomap(np, 0);
+	if (!acu->mpmu_base) {
+		pr_err("failed to map mpmu registers\n");
+		goto out;
+	}
+
+	acu->apmu_base = of_iomap(np, 1);
+	if (!acu->apmu_base) {
+		pr_err("failed to map apmu registers\n");
+		goto out;
+	}
+
+	acu->apbc_base = of_iomap(np, 2);
+	if (!acu->apbc_base) {
+		pr_err("failed to map apbc registers\n");
+		goto out;
+	}
+
+	acu->apbs_base = of_iomap(np, 3);
+	if (!acu->apbs_base) {
+		pr_err("failed to map apbs registers\n");
+		goto out;
+	}
+
+	acu->ciu_base = of_iomap(np, 4);
+	if (!acu->ciu_base) {
+		pr_err("failed to map ciu registers\n");
+		goto out;
+	}
+
+	acu->dciu_base = of_iomap(np, 5);
+	if (!acu->dciu_base) {
+		pr_err("failed to map dragon ciu registers\n");
+		goto out;
+	}
+
+	acu->ddrc_base = of_iomap(np, 6);
+	if (!acu->ddrc_base) {
+		pr_err("failed to map ddrc registers\n");
+		goto out;
+	}
+
+	return 0;
+out:
+	return -EINVAL;
+}
+
+static void __init aquilac_clk_init(struct device_node *np)
+{
+	int ret;
+	struct asr_clk_data *clock_data;
+
+	clock_data = kzalloc(sizeof(*clock_data), GFP_KERNEL);
+	if (!clock_data)
+		return;
+
+	ret = aquilac_clk_of_iomap(np, clock_data);
+	if (ret < 0)
+		goto out;
+
+	ret = asr_clk_init(np, &clock_data->unit, ASR_NR_CLKS);
+	if (ret < 0)
+		goto out;
+
+	aquilac_general_clk_init(clock_data);
+
+	aquilac_pll_init(clock_data);
+
+	aquilac_mix_clk_init(clock_data);
+
+	aquilac_periph_clk_init(clock_data);
+
+	asr_clks_enable((const char **)keep_on_clocks_tbl, ARRAY_SIZE(keep_on_clocks_tbl));
+
+	return;
+out:
+	kfree(clock_data);
+}
+CLK_OF_DECLARE(aquilac_clk, "asr,8751c-clock", aquilac_clk_init);
diff --git a/drivers/clk/asr/clk-gate.c b/drivers/clk/asr/clk-gate.c
new file mode 100644
index 0000000..4ba5587
--- /dev/null
+++ b/drivers/clk/asr/clk-gate.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * asr gate clock operation source file
+ *
+ * Copyright (C) 2019 ASR Microelectronics(Shanghai) Co., Ltd.
+ * Gang Wu <gangwu@...micro.com>
+ * Qiao Zhou <qiaozhou@...micro.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include "clk.h"
+
+#define to_clk_asr_gate(hw)	container_of(hw, struct asr_clk_gate, hw)
+
+#define TIMEOUT_LIMIT		20000
+#define DELAY_CYCLE		2000000
+
+static int asr_clk_gate_enable(struct clk_hw *hw)
+{
+	struct asr_clk_gate *gate = to_clk_asr_gate(hw);
+	unsigned long flags = 0;
+	unsigned long rate;
+	u32 tmp;
+	u32 val = 0;
+	int timeout_power = 1;
+
+	if (gate->lock)
+		spin_lock_irqsave(gate->lock, flags);
+
+	tmp = clk_readl(gate->reg);
+	tmp &= ~gate->mask;
+	tmp |= gate->val_enable;
+	clk_writel(tmp, gate->reg);
+
+	val = clk_readl(gate->reg);
+	while ((val & gate->mask) != gate->val_enable && (timeout_power < TIMEOUT_LIMIT)) {
+		udelay(timeout_power);
+		val = clk_readl(gate->reg);
+		timeout_power *= 10;
+	}
+
+	if (timeout_power > 1) {
+		if (val == tmp)
+			pr_err("write clk_gate %s timeout occur, read pass after %d us delay\n", clk_hw_get_name(hw), timeout_power);
+		else
+			panic("write clk_gate %s timeout after %d us!\n", clk_hw_get_name(hw), timeout_power);
+	}
+
+	if (gate->lock)
+		spin_unlock_irqrestore(gate->lock, flags);
+
+	if (gate->flags & ASR_CLK_GATE_NEED_DELAY) {
+		rate = clk_hw_get_rate(hw);
+		udelay(DIV_ROUND_UP(DELAY_CYCLE, rate));
+	}
+
+	return 0;
+}
+
+static void asr_clk_gate_disable(struct clk_hw *hw)
+{
+	struct asr_clk_gate *gate = to_clk_asr_gate(hw);
+	unsigned long flags = 0;
+	unsigned long rate;
+	u32 tmp;
+
+	if (gate->lock)
+		spin_lock_irqsave(gate->lock, flags);
+
+	tmp = clk_readl(gate->reg);
+	tmp &= ~gate->mask;
+	tmp |= gate->val_disable;
+	clk_writel(tmp, gate->reg);
+
+	if (gate->flags & ASR_CLK_GATE_NEED_DELAY) {
+		rate = clk_hw_get_rate(hw);
+		udelay(DIV_ROUND_UP(DELAY_CYCLE, rate));
+	}
+
+	if (gate->lock)
+		spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int asr_clk_gate_is_enabled(struct clk_hw *hw)
+{
+	struct asr_clk_gate *gate = to_clk_asr_gate(hw);
+	unsigned long flags = 0;
+	u32 tmp;
+
+	if (gate->lock)
+		spin_lock_irqsave(gate->lock, flags);
+
+	tmp = clk_readl(gate->reg);
+
+	if (gate->lock)
+		spin_unlock_irqrestore(gate->lock, flags);
+
+	return (tmp & gate->mask) == gate->val_enable;
+}
+
+const struct clk_ops asr_clk_gate_ops = {
+	.enable = asr_clk_gate_enable,
+	.disable = asr_clk_gate_disable,
+	.is_enabled = asr_clk_gate_is_enabled,
+};
+
+struct clk *asr_clk_register_gate(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable,
+		unsigned long gate_flags, spinlock_t *lock)
+{
+	struct asr_clk_gate *gate;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the gate */
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &asr_clk_gate_ops;
+	init.flags = flags | CLK_IS_BASIC;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	/* struct clk_gate assignments */
+	gate->reg = reg;
+	gate->mask = mask;
+	gate->val_enable = val_enable;
+	gate->val_disable = val_disable;
+	gate->flags = gate_flags;
+	gate->lock = lock;
+	gate->hw.init = &init;
+
+	clk = clk_register(dev, &gate->hw);
+
+	if (IS_ERR(clk))
+		kfree(gate);
+
+	return clk;
+}
diff --git a/drivers/clk/asr/clk-mix.c b/drivers/clk/asr/clk-mix.c
new file mode 100644
index 0000000..d099a41
--- /dev/null
+++ b/drivers/clk/asr/clk-mix.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * asr mix(div and mux) driver for asr-8751c
+ *
+ * Copyright (C) 2019 ASR Microelectronics(Shanghai) Co., Ltd.
+ * Gang Wu <gangwu@...micro.com>
+ * Qiao Zhou <qiaozhou@...micro.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include "clk.h"
+
+/*
+ * mux and div field can't be configured separately in two clocks.
+ */
+
+#define to_clk_mix(hw)	container_of(hw, struct asr_clk_mix, hw)
+
+static unsigned int _get_maxdiv(struct asr_clk_mix *mix)
+{
+	unsigned int div_mask = (1 << mix->reg_info.width_div) - 1;
+
+	if (mix->div_flags & CLK_DIVIDER_ONE_BASED)
+		return div_mask;
+	if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO)
+		return 1 << div_mask;
+
+	return div_mask + 1;
+}
+
+static unsigned int _get_div(struct asr_clk_mix *mix, unsigned int val)
+{
+
+	if (mix->div_flags & CLK_DIVIDER_ONE_BASED)
+		return val;
+	if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO)
+		return 1 << val;
+
+	return val + 1;
+}
+
+static unsigned int _get_div_val(struct asr_clk_mix *mix, unsigned int div)
+{
+	if (mix->div_flags & CLK_DIVIDER_ONE_BASED)
+		return div;
+	if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO)
+		return __ffs(div);
+
+	return div - 1;
+}
+
+static void _filter_clk_table(struct asr_clk_mix *mix,
+				struct asr_clk_mix_clk_table *table,
+				unsigned int table_size)
+{
+	int i;
+	struct asr_clk_mix_clk_table *item;
+	struct clk_hw *parent, *hw;
+	unsigned long parent_rate;
+
+	hw = &mix->hw;
+
+	for (i = 0; i < table_size; i++) {
+		item = &table[i];
+		parent = clk_hw_get_parent_by_index(hw, item->parent_index);
+		parent_rate = clk_hw_get_rate(parent);
+		if (parent_rate % item->rate) {
+			item->valid = 0;
+		} else {
+			item->divisor = parent_rate / item->rate;
+			item->valid = 1;
+		}
+	}
+}
+
+static int _set_rate(struct asr_clk_mix *mix, u32 mux_val, u32 div_val,
+			unsigned int change_mux, unsigned int change_div)
+{
+	struct asr_clk_mix_reg_info *ri = &mix->reg_info;
+	u8 width, shift;
+	u32 mux_div;
+	unsigned long flags = 0;
+
+	if (!change_mux && !change_div)
+		return -EINVAL;
+
+	if (mix->lock)
+		spin_lock_irqsave(mix->lock, flags);
+
+	mux_div = clk_readl(ri->reg_clk_ctrl);
+
+	if (change_div) {
+		width = ri->width_div;
+		shift = ri->shift_div;
+		mux_div &= ~ASR_CLK_BITS_MASK(width, shift);
+		mux_div |= ASR_CLK_BITS_SET_VAL(div_val, width, shift);
+	}
+
+	if (change_mux) {
+		width = ri->width_mux;
+		shift = ri->shift_mux;
+		mux_div &= ~ASR_CLK_BITS_MASK(width, shift);
+		mux_div |= ASR_CLK_BITS_SET_VAL(mux_val, width, shift);
+	}
+
+	clk_writel(mux_div, ri->reg_clk_ctrl);
+
+	if (mix->lock)
+		spin_unlock_irqrestore(mix->lock, flags);
+
+	return 0;
+}
+
+static int asr_clk_mix_determine_rate(struct clk_hw *hw,
+				      struct clk_rate_request *req)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+	struct asr_clk_mix_clk_table *item;
+	struct clk_hw *parent, *parent_best;
+	unsigned long parent_rate, mix_rate, mix_rate_best, parent_rate_best;
+	unsigned long gap, gap_best;
+	u32 div_val_max;
+	unsigned int div;
+	int i, j;
+
+	mix_rate_best = 0;
+	parent_rate_best = 0;
+	gap_best = ULONG_MAX;
+	parent_best = NULL;
+
+	if (mix->table) {
+		for (i = 0; i < mix->table_size; i++) {
+			item = &mix->table[i];
+			if (item->valid == 0)
+				continue;
+			parent = clk_hw_get_parent_by_index(hw,
+							item->parent_index);
+			parent_rate = clk_hw_get_rate(parent);
+			mix_rate = parent_rate / item->divisor;
+			gap = abs(mix_rate - req->rate);
+			if (parent_best == NULL || gap < gap_best) {
+				parent_best = parent;
+				parent_rate_best = parent_rate;
+				mix_rate_best = mix_rate;
+				gap_best = gap;
+				if (gap_best == 0)
+					goto found;
+			}
+		}
+	} else {
+		for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+			parent = clk_hw_get_parent_by_index(hw, i);
+			parent_rate = clk_hw_get_rate(parent);
+			div_val_max = _get_maxdiv(mix);
+			for (j = 0; j < div_val_max; j++) {
+				div = _get_div(mix, j);
+				mix_rate = parent_rate / div;
+				gap = abs(mix_rate - req->rate);
+				if (parent_best == NULL || gap < gap_best) {
+					parent_best = parent;
+					parent_rate_best = parent_rate;
+					mix_rate_best = mix_rate;
+					gap_best = gap;
+					if (gap_best == 0)
+						goto found;
+				}
+			}
+		}
+	}
+
+found:
+	if (!parent_best)
+		return -EINVAL;
+
+	req->best_parent_rate = parent_rate_best;
+	req->best_parent_hw = parent_best;
+	req->rate = mix_rate_best;
+
+	return 0;
+}
+
+static int asr_clk_mix_set_rate_and_parent(struct clk_hw *hw,
+						unsigned long rate,
+						unsigned long parent_rate,
+						u8 index)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+	unsigned int div;
+	u32 div_val;
+
+	div = parent_rate / rate;
+	div_val = _get_div_val(mix, div);
+
+	return _set_rate(mix, index, div_val, 1, 1);
+}
+
+static u8 asr_clk_mix_get_parent(struct clk_hw *hw)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+	struct asr_clk_mix_reg_info *ri = &mix->reg_info;
+	unsigned long flags = 0;
+	u32 mux_div = 0;
+	u8 width, shift;
+	u32 mux_val;
+
+	if (mix->lock)
+		spin_lock_irqsave(mix->lock, flags);
+
+	mux_div = clk_readl(ri->reg_clk_ctrl);
+
+	if (mix->lock)
+		spin_unlock_irqrestore(mix->lock, flags);
+
+	width = mix->reg_info.width_mux;
+	shift = mix->reg_info.shift_mux;
+
+	mux_val = ASR_CLK_BITS_GET_VAL(mux_div, width, shift);
+
+	return mux_val;
+}
+
+static unsigned long asr_clk_mix_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+	struct asr_clk_mix_reg_info *ri = &mix->reg_info;
+	unsigned long flags = 0;
+	u32 mux_div = 0;
+	u8 width, shift;
+	unsigned int div;
+
+	if (mix->lock)
+		spin_lock_irqsave(mix->lock, flags);
+
+	mux_div = clk_readl(ri->reg_clk_ctrl);
+
+	if (mix->lock)
+		spin_unlock_irqrestore(mix->lock, flags);
+
+	width = mix->reg_info.width_div;
+	shift = mix->reg_info.shift_div;
+
+	div = _get_div(mix, ASR_CLK_BITS_GET_VAL(mux_div, width, shift));
+
+	return parent_rate / div;
+}
+
+static int asr_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+	struct asr_clk_mix_clk_table *item;
+	int i;
+	u32 div_val, mux_val;
+
+	if (mix->table) {
+		for (i = 0; i < mix->table_size; i++) {
+			item = &mix->table[i];
+			if (item->valid == 0)
+				continue;
+			if (item->parent_index == index)
+				break;
+		}
+		if (i < mix->table_size) {
+			div_val = _get_div_val(mix, item->divisor);
+			mux_val = item->parent_index;
+		} else
+			return -EINVAL;
+	} else {
+		mux_val = index;
+		div_val = 0;
+	}
+
+	return _set_rate(mix, mux_val, div_val, 1, div_val ? 1 : 0);
+}
+
+static int asr_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long best_parent_rate)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+	struct asr_clk_mix_clk_table *item;
+	unsigned long parent_rate;
+	unsigned int best_divisor;
+	struct clk_hw *parent;
+	int i;
+
+	best_divisor = best_parent_rate / rate;
+
+	if (mix->table) {
+		for (i = 0; i < mix->table_size; i++) {
+			item = &mix->table[i];
+			if (item->valid == 0)
+				continue;
+			parent = clk_hw_get_parent_by_index(hw,
+							item->parent_index);
+			parent_rate = clk_hw_get_rate(parent);
+			if (parent_rate == best_parent_rate
+				&& item->divisor == best_divisor)
+				break;
+		}
+		if (i < mix->table_size)
+			return _set_rate(mix, item->parent_index,
+					_get_div_val(mix, item->divisor),
+					1, 1);
+		else
+			return -EINVAL;
+	} else {
+		for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+			parent = clk_hw_get_parent_by_index(hw, i);
+			parent_rate = clk_hw_get_rate(parent);
+			if (parent_rate == best_parent_rate)
+				break;
+		}
+		if (i < clk_hw_get_num_parents(hw))
+			return _set_rate(mix, i,
+					_get_div_val(mix, best_divisor), 1, 1);
+		else
+			return -EINVAL;
+	}
+}
+
+static void asr_clk_mix_init(struct clk_hw *hw)
+{
+	struct asr_clk_mix *mix = to_clk_mix(hw);
+
+	if (mix->table)
+		_filter_clk_table(mix, mix->table, mix->table_size);
+}
+
+const struct clk_ops asr_clk_mix_ops = {
+	.determine_rate = asr_clk_mix_determine_rate,
+	.set_rate_and_parent = asr_clk_mix_set_rate_and_parent,
+	.set_rate = asr_clk_set_rate,
+	.set_parent = asr_clk_set_parent,
+	.get_parent = asr_clk_mix_get_parent,
+	.recalc_rate = asr_clk_mix_recalc_rate,
+	.init = asr_clk_mix_init,
+};
+
+struct clk *asr_clk_register_mix(struct device *dev,
+					const char *name,
+					const char **parent_names,
+					u8 num_parents,
+					unsigned long flags,
+					struct asr_clk_mix_config *config,
+					spinlock_t *lock)
+{
+	struct asr_clk_mix *mix;
+	struct clk *clk;
+	struct clk_init_data init;
+	size_t table_bytes;
+
+	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
+	if (!mix)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.flags = flags | CLK_GET_RATE_NOCACHE;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.ops = &asr_clk_mix_ops;
+
+	memcpy(&mix->reg_info, &config->reg_info, sizeof(config->reg_info));
+	if (config->table) {
+		table_bytes = sizeof(*config->table) * config->table_size;
+		mix->table = kmemdup(config->table,
+						table_bytes, GFP_KERNEL);
+		if (!mix->table) {
+			kfree(mix);
+			return ERR_PTR(-ENOMEM);
+		}
+		mix->table_size = config->table_size;
+	}
+
+	mix->lock = lock;
+	mix->hw.init = &init;
+
+	clk = clk_register(dev, &mix->hw);
+
+	if (IS_ERR(clk)) {
+		kfree(mix->table);
+		kfree(mix);
+	}
+
+	return clk;
+}
diff --git a/drivers/clk/asr/clk-pll.c b/drivers/clk/asr/clk-pll.c
new file mode 100644
index 0000000..2087fbb
--- /dev/null
+++ b/drivers/clk/asr/clk-pll.c
@@ -0,0 +1,642 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * clock pll driver file for asr-8751c
+ *
+ * Copyright (C) 2019 ASR Microelectronics(Shanghai) Co., Ltd.
+ * Kevin Liu <kevinliu@...micro.com>
+ * Qiao	Zhou <qiaozhou@...micro.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include "clk.h"
+#include "clk-pll.h"
+
+#define pll_readl_cr(p) clk_readl(p->params->cr_reg)
+#define pll_readl_pll_swcr(p) clk_readl(p->params->pll_swcr)
+#define pll_readl_pll_swcr2(p) clk_readl(p->params->pll_swcr2)
+#define pll_writel_cr(val, p) clk_writel(val, p->params->cr_reg)
+#define pll_writel_pll_swcr(val, p) clk_writel(val, p->params->pll_swcr)
+#define pll_writel_pll_swcr2(val, p) clk_writel(val, p->params->pll_swcr2)
+
+/* unified pllx_cr for pll2~5 */
+union pllx_cr {
+	struct {
+		unsigned int frcdiv:8;
+		unsigned int fbdiv:7;
+		unsigned int reserved:4;
+		unsigned int pu:1;
+		unsigned int reserved1:4;
+		unsigned int ssd:4;
+		unsigned int ssdx2:1;
+		unsigned int folock_en:1;
+		unsigned int ssmod:2;
+	} b;
+	unsigned int v;
+};
+
+/* unified pll SW control register for pll1~5 */
+union pllx_swcr {
+	struct {
+		unsigned int reserved:7;
+		unsigned int band_sel:1;
+		unsigned int pumpcur:2;
+		unsigned int falock_en:1;
+		unsigned int kvco:3;
+		unsigned int regvol:2;
+		unsigned int bypass:1;
+		unsigned int lockdly:3;
+		unsigned int folock_en:1;
+		unsigned int mod_sel:1;
+		unsigned int te_sel:2;
+		unsigned int at_en:1;
+		unsigned int dt_en:1;
+		unsigned int div1_en:1;
+		unsigned int div2_en:1;
+		unsigned int div3_en:1;
+		unsigned int div4_en:1;
+		unsigned int div5_en:1;
+		unsigned int clk_en:1;
+	} b;
+	unsigned int v;
+};
+
+/* pll SW control register for pll6/7 */
+union pllx_swcr_new {
+	struct {
+		unsigned int fbdiv:7;
+		unsigned int band_sel:1;
+		unsigned int pumpcur:2;
+		unsigned int falock_en:1;
+		unsigned int kvco:3;
+		unsigned int regvol:2;
+		unsigned int bypass:1;
+		unsigned int lockdly:3;
+		unsigned int folock_en:1;
+		unsigned int mod_sel:1;
+		unsigned int te_sel:2;
+		unsigned int at_en:1;
+		unsigned int dt_en:1;
+		unsigned int div1_en:1;
+		unsigned int div2_en:1;
+		unsigned int div3_en:1;
+		unsigned int div4_en:1;
+		unsigned int div5_en:1;
+		unsigned int pu:1;
+	} b;
+	unsigned int v;
+};
+
+/* pll SW control register2 for pll6 */
+union pllx_swcr2 {
+	struct {
+		unsigned int frcdiv:8;
+		unsigned int ssd:4;
+		unsigned int ssdx2:1;
+		unsigned int folock_en:1;
+		unsigned int ssmod:2;
+		unsigned int reserved:15;
+		unsigned int locked:1;
+	} b;
+	unsigned int v;
+};
+
+/* pll SW control register2 for pll7 */
+union pllx_swcr2_frac {
+	struct {
+		unsigned int frcdiv:24;
+		unsigned int reserved:7;
+		unsigned int locked:1;
+	} b;
+	unsigned int v;
+};
+
+static struct kvco_range kvco_rng_table[] = {
+	/* High band */
+	{2620, 2780, 0x7, 1},
+	{2470, 2620, 0x6, 1},
+	{2320, 2470, 0x5, 1},
+	{2140, 2320, 0x4, 1},
+	{1970, 2140, 0x3, 1},
+	{1820, 1970, 0x2, 1},
+	{1670, 1820, 0x1, 1},
+	{1510, 1670, 0x0, 1},
+	/* Low band - prefer high band */
+	{1600, 1760, 0x7, 0},
+	{1450, 1600, 0x6, 0},
+	{1300, 1450, 0x5, 0},
+	{1140, 1300, 0x4, 0},
+	{970, 1140, 0x3, 0},
+	{820, 970, 0x2, 0},
+	{680, 820, 0x1, 0},
+	{540, 680, 0x0, 0},
+};
+
+static int __pll_is_enabled(struct clk_hw *hw)
+{
+	struct clk_vco *vco = to_clk_vco(hw);
+	union pllx_cr pllx_cr;
+	union pllx_swcr_new pllx_swcr_new;
+	unsigned int enabled;
+
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr_new.v = pll_readl_pll_swcr(vco);
+		enabled = pllx_swcr_new.b.pu;
+	} else {
+		pllx_cr.v = pll_readl_cr(vco);
+		enabled = pllx_cr.b.pu;
+	}
+
+	return enabled;
+}
+
+static void __clk_vco_rate2reg(struct clk_vco *vco, unsigned long rate,
+			       unsigned int *kvco, unsigned int *band)
+{
+	int i, size;
+
+	size = ARRAY_SIZE(kvco_rng_table);
+
+	for (i = 0; i < size; i++) {
+		if (rate >= kvco_rng_table[i].vco_min &&
+		    rate <= kvco_rng_table[i].vco_max) {
+			*kvco = kvco_rng_table[i].kvco;
+			*band = kvco_rng_table[i].band;
+			return;
+		}
+	}
+	if (i == size)
+		panic("can't find correct vco\n");
+}
+
+/* frequency unit Mhz, return pll vco freq */
+static unsigned long __get_vco_freq(struct clk_hw *hw)
+{
+	unsigned int pllx_vco, pllxfrcd, pllxfbd;
+	unsigned long pllx_vco_l, rate;
+	struct clk_vco *vco = to_clk_vco(hw);
+	union pllx_cr pllx_cr;
+	union pllx_swcr_new pllx_swcr_new;
+	union pllx_swcr pllx_swcr;
+	union pllx_swcr2 pllx_swcr2;
+	union pllx_swcr2_frac pllx_swcr2_frac;
+	bool hiband;
+
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr_new.v = pll_readl_pll_swcr(vco);
+		pllxfbd = pllx_swcr_new.b.fbdiv & 0x7f;
+		if (vco->flags & ASR_PLL_FRAC_AUDIO) {
+			pllx_swcr2_frac.v = pll_readl_pll_swcr2(vco);
+			pllxfrcd = pllx_swcr2_frac.b.frcdiv & 0x3fffff;
+		} else {
+			pllx_swcr2.v = pll_readl_pll_swcr2(vco);
+			pllxfrcd = pllx_swcr2.b.frcdiv & 0xff;
+		}
+		hiband = !!pllx_swcr_new.b.band_sel;
+	} else {
+		pllx_cr.v = pll_readl_cr(vco);
+		pllxfrcd = pllx_cr.b.frcdiv & 0xff;
+		pllxfbd = pllx_cr.b.fbdiv & 0x7f;
+		pllx_swcr.v = pll_readl_pll_swcr(vco);
+		hiband = !!pllx_swcr.b.band_sel;
+	}
+
+	if (pllxfrcd) {
+		if (((vco->flags & ASR_PLL_REG_NEW) && (vco->flags & ASR_PLL_FRAC_AUDIO))) {
+			if (pllxfrcd <= (1 << 20)) {
+				pllx_vco_l = (unsigned long)pllxfbd * 26 * MHZ +
+						DIV_ROUND_UP((unsigned long)pllxfrcd * 26 * MHZ, (1 << 20));
+			} else if ((pllxfrcd >= (3 << 20)) && (pllxfbd >= 1)) {
+				pllx_vco_l = (unsigned long)pllxfbd * 26 * MHZ -
+						DIV_ROUND_UP(((1 << 22) - (unsigned long)pllxfrcd) * 26 * MHZ, (1 << 20));
+			} else {
+				pr_err("%s fraction divider %u is invalid\n", __clk_get_name(hw->clk), pllxfrcd);
+				panic("invalid fraction divider\n");
+			}
+			rate = DIV_ROUND_UP(pllx_vco_l, 50) * 50;
+		} else {
+			if (pllxfrcd <= (1 << 6)) {
+				pllx_vco = pllxfbd * 26 + DIV_ROUND_UP(pllxfrcd * 26, 64);
+			} else if ((pllxfrcd >= (3 << 6)) && (pllxfbd >= 1)) {
+				pllx_vco = pllxfbd * 26 + DIV_ROUND_UP(pllxfrcd * 26, 64) - 104;
+			} else {
+				pr_err("%s fraction divider %u is invalid\n", __clk_get_name(hw->clk), pllxfrcd);
+				panic("invalid fraction divider\n");
+			}
+			rate = pllx_vco * MHZ;
+		}
+	} else {
+		rate = 26 * MHZ * pllxfbd;
+	}
+
+	if (hiband)
+		rate = rate * 2;
+
+	return rate;
+}
+
+static void __pll_vco_cfg(struct clk_vco *vco)
+{
+	union pllx_swcr pllx_swcr;
+	union pllx_swcr_new pllx_swcr_new;
+
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr_new.v = pll_readl_pll_swcr(vco);
+		pllx_swcr_new.b.at_en = 0;
+		pllx_swcr_new.b.dt_en = 0;
+		pllx_swcr_new.b.te_sel = 0;
+		pllx_swcr_new.b.mod_sel = (vco->flags & ASR_PLL_FRAC_FEAT) ? 0 : 1;
+		pllx_swcr_new.b.folock_en = 0;
+		pllx_swcr_new.b.lockdly = 1;
+		pllx_swcr_new.b.bypass = 0;
+		pllx_swcr_new.b.regvol = 1;
+		pllx_swcr_new.b.falock_en = 1;
+		pllx_swcr_new.b.pumpcur = 1;
+		pll_writel_pll_swcr(pllx_swcr_new.v, vco);
+	} else {
+		pllx_swcr.v = pll_readl_pll_swcr(vco);
+		pllx_swcr.b.at_en = 0;
+		pllx_swcr.b.dt_en = 0;
+		pllx_swcr.b.te_sel = 0;
+		pllx_swcr.b.mod_sel = (vco->flags & ASR_PLL_FRAC_FEAT) ? 0 : 1;
+		pllx_swcr.b.folock_en = 0;
+		pllx_swcr.b.lockdly = 1;
+		pllx_swcr.b.bypass = 0;
+		pllx_swcr.b.regvol = 1;
+		pllx_swcr.b.falock_en = 1;
+		pllx_swcr.b.pumpcur = 1;
+		pll_writel_pll_swcr(pllx_swcr.v, vco);
+	}
+}
+
+/* FIXME: set preset parameters directly rather than compute from rate */
+static void enable_pll_ssc(struct clk_vco *vco)
+{
+	struct asr_vco_params *vco_params = vco->params;
+	union pllx_cr pllx_cr;
+	unsigned long flags;
+	union pllx_swcr2 pllx_swcr2;
+
+	spin_lock_irqsave(vco->lock, flags);
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr2.v = pll_readl_pll_swcr2(vco);
+		pllx_swcr2.b.ssd = vco_params->ssc_depth & 0xf;
+		pllx_swcr2.b.ssdx2 = vco_params->ssc_depx2_en ? 0x1 : 0x0;
+		pllx_swcr2.b.folock_en = vco_params->ssc_folock_en ? 0x1 : 0x0;
+		pllx_swcr2.b.ssmod = vco_params->ssc_mode & 0x3;
+		pll_writel_pll_swcr2(pllx_swcr2.v, vco);
+	} else {
+		pllx_cr.v = pll_readl_cr(vco);
+		/* SSC will be enabled if scc depth is not 0 */
+		pllx_cr.b.ssd = vco_params->ssc_depth & 0xf;
+		pllx_cr.b.ssdx2 = vco_params->ssc_depx2_en ? 0x1 : 0x0;
+		pllx_cr.b.folock_en = vco_params->ssc_folock_en ? 0x1 : 0x0;
+		pllx_cr.b.ssmod = vco_params->ssc_mode & 0x3;
+		pll_writel_cr(pllx_cr.v, vco);
+	}
+	spin_unlock_irqrestore(vco->lock, flags);
+}
+
+static void disable_pll_ssc(struct clk_vco *vco)
+{
+	struct asr_vco_params *vco_params = vco->params;
+	union pllx_cr pllx_cr;
+	unsigned long flags;
+	union pllx_swcr2 pllx_swcr2;
+
+	spin_lock_irqsave(vco->lock, flags);
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr2.v = pll_readl_pll_swcr2(vco);
+		pllx_swcr2.b.ssd = 0x0;
+		pllx_swcr2.b.ssdx2 = 0x0;
+		pllx_swcr2.b.folock_en = 0x0;
+		pllx_swcr2.b.ssmod = vco_params->ssc_mode & 0x3;
+		pll_writel_pll_swcr2(pllx_swcr2.v, vco);
+	} else {
+		pllx_cr.v = pll_readl_cr(vco);
+		/* SSC will be disabled if scc depth is 0 */
+		pllx_cr.b.ssd = 0x0;
+		pllx_cr.b.ssdx2 = 0x0;
+		pllx_cr.b.folock_en = 0x0;
+		pllx_cr.b.ssmod = 0x0;
+		pll_writel_cr(pllx_cr.v, vco);
+	}
+	spin_unlock_irqrestore(vco->lock, flags);
+}
+
+static void clk_pll_vco_init(struct clk_hw *hw)
+{
+	struct clk_vco *vco = to_clk_vco(hw);
+	unsigned long vco_rate;
+	unsigned int vco_rngl, vco_rngh, tmp;
+	struct asr_vco_params *params = vco->params;
+
+	if (!__pll_is_enabled(hw)) {
+		__pll_vco_cfg(vco);
+	} else {
+		vco_rate = __get_vco_freq(hw) / MHZ;
+		if (!(vco->flags & ASR_PLL_SKIP_DEF_RATE)) {
+			/* check whether vco is in the range of 2% our expectation */
+			tmp = params->default_rate / MHZ;
+			if (tmp != vco_rate) {
+				vco_rngh = tmp + tmp * 2 / 100;
+				vco_rngl = tmp - tmp * 2 / 100;
+				if (!((vco_rngl <= vco_rate) &&
+					 (vco_rate <= vco_rngh)))
+					panic("wrong vco_rate\n");
+			}
+		}
+		/* Make sure SSC is enabled if pll is on */
+		if (vco->flags & ASR_PLL_SSC_FEAT) {
+			enable_pll_ssc(vco);
+			params->ssc_enabled = true;
+		}
+		pr_debug("%s is enabled @ %lu\n", __clk_get_name(hw->clk), vco_rate * MHZ);
+	}
+}
+
+static int clk_pll_vco_enable(struct clk_hw *hw)
+{
+	unsigned int delaytime = 50;
+	unsigned long flags;
+	struct clk_vco *vco = to_clk_vco(hw);
+	struct asr_vco_params *params = vco->params;
+	union pllx_cr pllx_cr;
+	union pllx_swcr_new pllx_swcr_new;
+
+	if (__pll_is_enabled(hw))
+		return 0;
+
+	spin_lock_irqsave(vco->lock, flags);
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr_new.v = pll_readl_pll_swcr(vco);
+		pllx_swcr_new.b.pu = 1;
+		pll_writel_pll_swcr(pllx_swcr_new.v, vco);
+	} else {
+		pllx_cr.v = pll_readl_cr(vco);
+		pllx_cr.b.pu = 1;
+		pll_writel_cr(pllx_cr.v, vco);
+	}
+	spin_unlock_irqrestore(vco->lock, flags);
+
+	/* check lock status */
+	udelay(50);
+	while ((!(__raw_readl(params->lock_reg) & params->lock_enable_bit))
+	       && delaytime) {
+		udelay(5);
+		delaytime--;
+	}
+
+	/* PLL can't get to stable status in time on emulators or FPGA */
+	if (unlikely(!delaytime)) {
+		pr_err("%s enabling didn't get stable within 300us!!!\n", __clk_get_name(hw->clk));
+		return -EBUSY;
+	}
+
+	if (vco->flags & ASR_PLL_SSC_FEAT)
+		if (((vco->flags & ASR_PLL_SSC_AON) && !params->ssc_enabled)
+		    || !(vco->flags & ASR_PLL_SSC_AON)) {
+			enable_pll_ssc(vco);
+			params->ssc_enabled = true;
+		}
+
+	return 0;
+}
+
+static void clk_pll_vco_disable(struct clk_hw *hw)
+{
+	unsigned long flags;
+	struct clk_vco *vco = to_clk_vco(hw);
+	struct asr_vco_params *params = vco->params;
+	union pllx_cr pllx_cr;
+	union pllx_swcr_new pllx_swcr_new;
+
+	spin_lock_irqsave(vco->lock, flags);
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr_new.v = pll_readl_pll_swcr(vco);
+		pllx_swcr_new.b.pu = 0;
+		pll_writel_pll_swcr(pllx_swcr_new.v, vco);
+	} else {
+		pllx_cr.v = pll_readl_cr(vco);
+		pllx_cr.b.pu = 0;
+		pll_writel_cr(pllx_cr.v, vco);
+	}
+	spin_unlock_irqrestore(vco->lock, flags);
+
+	if ((vco->flags & ASR_PLL_SSC_FEAT) &&
+	    !(vco->flags & ASR_PLL_SSC_AON)) {
+		disable_pll_ssc(vco);
+		params->ssc_enabled = false;
+	}
+}
+
+/*
+ * pll rate change requires sequence:
+ * clock off -> change rate setting -> clock on
+ * This function doesn't really change rate, but cache the config
+ */
+static int clk_pll_vco_setrate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long parent_rate)
+{
+	unsigned int i, kvco = 0, band, fbd, frcd;
+	unsigned long flags;
+	unsigned long new_rate = rate, old_rate;
+	struct clk_vco *vco = to_clk_vco(hw);
+	struct asr_vco_params *params = vco->params;
+	union pllx_swcr pllx_swcr;
+	union pllx_swcr_new pllx_swcr_new;
+	union pllx_swcr2 pllx_swcr2;
+	union pllx_swcr2_frac pllx_swcr2_frac;
+	union pllx_cr pllx_cr;
+
+	old_rate = __get_vco_freq(hw);
+	/* setp 1: calculate fbd frcd kvco and band */
+	if (params->freq_table) {
+		for (i = 0; i < params->freq_table_size; i++) {
+			if (rate == params->freq_table[i].output_rate) {
+				kvco = params->freq_table[i].kvco;
+				band = params->freq_table[i].band;
+				frcd = params->freq_table[i].frcd;
+				fbd = params->freq_table[i].fbd;
+				break;
+			}
+		}
+		if (i == params->freq_table_size)
+			panic("can't find correct vco\n");
+	} else {
+		__clk_vco_rate2reg(vco, rate / MHZ, &kvco, &band);
+		if (band)
+			rate = rate / 2;
+		if (vco->flags & ASR_PLL_FRAC_AUDIO) {
+			/* Fraction audio PLL diviation is within 24Hz so round the rate by 50Hz */
+			if (rate % 50) {
+				rate = rate / 50 * 50;
+				pr_debug("%s Truncate the rate set for %s from %luHz to %luHz\n",
+					__func__, __clk_get_name(hw->clk), new_rate, band ? rate * 2 : rate);
+				new_rate = band ? (rate * 2) : rate;
+			}
+			fbd = rate / (26 * MHZ);
+			frcd = rate % (26 * MHZ);
+			if (frcd > 13 * MHZ) {
+				fbd++;
+				frcd = (3 << 20) + (unsigned long)frcd * (1 << 20) / (26 * MHZ);
+			} else {
+				frcd = (unsigned long)frcd * (1 << 20) / (26 * MHZ);
+			}
+		} else {
+			/* The other PLL diviation is within 1Mhz so round the rate by 1Mhz */
+			if (rate % MHZ) {
+				rate = rate / MHZ * MHZ;
+				pr_debug("%s Truncate the rate set for %s from %luHz to %luHz\n",
+					__func__, __clk_get_name(hw->clk), new_rate, band ? rate * 2 : rate);
+				new_rate = band ? (rate * 2) : rate;
+			}
+			rate /= MHZ;
+			fbd = rate / 26;
+			frcd = rate % 26;
+			if (frcd > 13) {
+				fbd++;
+				frcd = 192 + (frcd * 64 / 26);
+			} else {
+				frcd = frcd * 64 / 26;
+			}
+		}
+	}
+
+	spin_lock_irqsave(vco->lock, flags);
+	/* setp 2: set pll kvco/band and fbd/frcd setting */
+	if (vco->flags & ASR_PLL_REG_NEW) {
+		pllx_swcr_new.v = pll_readl_pll_swcr(vco);
+		pllx_swcr_new.b.kvco = kvco & 0x7;
+		pllx_swcr_new.b.band_sel = band & 0x1;
+		pllx_swcr_new.b.fbdiv = fbd & 0x7f;
+		pll_writel_pll_swcr(pllx_swcr_new.v, vco);
+		if (vco->flags & ASR_PLL_FRAC_AUDIO) {
+			pllx_swcr2_frac.v = pll_readl_pll_swcr2(vco);
+			pllx_swcr2_frac.b.frcdiv = frcd & 0x3fffff;
+			pll_writel_pll_swcr2(pllx_swcr2_frac.v, vco);
+		} else {
+			pllx_swcr2.v = pll_readl_pll_swcr2(vco);
+			pllx_swcr2.b.frcdiv = frcd & 0xff;
+			pll_writel_pll_swcr2(pllx_swcr2.v, vco);
+		}
+	} else {
+		pllx_swcr.v = pll_readl_pll_swcr(vco);
+		pllx_swcr.b.kvco = kvco & 0x7;
+		pllx_swcr.b.band_sel = band & 0x1;
+		pll_writel_pll_swcr(pllx_swcr.v, vco);
+		pllx_cr.v = pll_readl_cr(vco);
+		pllx_cr.b.frcdiv = frcd & 0xff;
+		pllx_cr.b.fbdiv = fbd & 0x7f;
+		pll_writel_cr(pllx_cr.v, vco);
+	}
+	spin_unlock_irqrestore(vco->lock, flags);
+
+	pr_debug("%s %s rate %lu->%lu!\n", __func__,
+		 __clk_get_name(hw->clk), old_rate, new_rate);
+	return 0;
+}
+
+static unsigned long clk_vco_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	return __get_vco_freq(hw);
+}
+
+static long clk_vco_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *prate)
+{
+	struct clk_vco *vco = to_clk_vco(hw);
+	unsigned long max_rate = 0, new_rate = rate;
+	unsigned int i, band, kvco;
+	struct asr_vco_params *params = vco->params;
+
+	if (rate > params->vco_max || rate < params->vco_min) {
+		pr_err("%lu rate out of range!\n", rate);
+		return -EINVAL;
+	}
+
+	if (params->freq_table) {
+		for (i = 0; i < params->freq_table_size; i++) {
+			if (params->freq_table[i].output_rate <= rate) {
+				if (max_rate <
+				    params->freq_table[i].output_rate)
+					max_rate =
+					    params->freq_table[i].output_rate;
+			}
+		}
+	} else {
+		__clk_vco_rate2reg(vco, rate / MHZ, &kvco, &band);
+		if (band)
+			rate = rate / 2;
+		if (vco->flags & ASR_PLL_FRAC_AUDIO) {
+			/* Fraction audio PLL diviation is within 24Hz so round the rate by 50Hz */
+			if (rate % 50) {
+				rate = rate / 50 * 50;
+				pr_debug("%s Truncate the rate set for %s from %luHz to %luHz\n",
+					__func__, __clk_get_name(hw->clk), new_rate, band ? rate * 2 : rate);
+			}
+		} else {
+			/* The other PLL diviation is within 1Mhz so round the rate by 1Mhz */
+			if (rate % MHZ) {
+				rate = rate / MHZ * MHZ;
+				pr_debug("%s Truncate the rate set for %s from %luHz to %luHz\n",
+					__func__, __clk_get_name(hw->clk), new_rate, band ? rate * 2 : rate);
+			}
+		}
+		if (band)
+			max_rate = rate * 2;
+		else
+			max_rate = rate;
+	}
+	return max_rate;
+}
+
+static const struct clk_ops clk_vco_ops = {
+	.init = clk_pll_vco_init,
+	.enable = clk_pll_vco_enable,
+	.disable = clk_pll_vco_disable,
+	.set_rate = clk_pll_vco_setrate,
+	.recalc_rate = clk_vco_recalc_rate,
+	.round_rate = clk_vco_round_rate,
+	.is_enabled = __pll_is_enabled,
+};
+
+struct clk *asr_clk_register_vco(const char *name,
+				    const char *parent_name,
+				    unsigned long flags, u32 vco_flags,
+				    spinlock_t *lock,
+				    struct asr_vco_params *params)
+{
+	struct clk_vco *vco;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	vco = kzalloc(sizeof(*vco), GFP_KERNEL);
+	if (!vco)
+		return NULL;
+
+	init.name = name;
+	init.ops = &clk_vco_ops;
+	init.flags = flags | CLK_SET_RATE_GATE;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	vco->flags = vco_flags;
+	vco->lock = lock;
+	vco->hw.init = &init;
+	vco->params = params;
+
+	clk = clk_register(NULL, &vco->hw);
+	if (IS_ERR(clk))
+		kfree(vco);
+
+	return clk;
+}
diff --git a/drivers/clk/asr/clk-pll.h b/drivers/clk/asr/clk-pll.h
new file mode 100644
index 0000000..6c66540
--- /dev/null
+++ b/drivers/clk/asr/clk-pll.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASR_CLK_PLL_H
+#define __ASR_CLK_PLL_H
+
+/*
+ * struct kvco_range -store kvco and vrng for different frequency range
+ * @vco_min:	min frequency of vco
+ * @vco_max:	max frequency of vco
+ * @kvco:	kvco val for relevant frequency range
+ * @band:	high band or low band
+ */
+struct kvco_range {
+	int vco_min;
+	int vco_max;
+	u8 kvco;
+	u8 band;
+};
+
+struct asr_vco_freq_table {
+	unsigned long output_rate;
+	u16 frcd;
+	u16 fbd;
+	u8 kvco;
+	u8 band;
+};
+
+enum ssc_mode {
+	CENTER_SPREAD = 0x0,
+	UP_SPREAD = 0x1,
+	DOWN_SPREAD = 0x2,
+};
+
+struct asr_vco_params {
+	unsigned long vco_min;
+	unsigned long vco_max;
+	void __iomem *cr_reg;
+	void __iomem *pll_swcr;
+	void __iomem *pll_swcr2;
+	void __iomem *dpll_swcr;
+	void __iomem *dpll_swcr2;
+	void __iomem *dpll_ctrl;
+	void __iomem *lock_reg;
+	u32 cr_off;
+	u32 swcr_off;
+	u32 swcr2_off;
+	u32 lock_off;
+	u32 lock_enable_bit;
+	unsigned long default_rate;
+
+	struct asr_vco_freq_table *freq_table;
+	int freq_table_size;
+
+	/* SSC setting */
+	bool ssc_enabled;
+	enum ssc_mode ssc_mode;
+	unsigned int ssc_folock_en;
+	unsigned int ssc_depx2_en;
+	unsigned int ssc_depth;
+};
+
+struct clk_vco {
+	struct clk_hw hw;
+	spinlock_t *lock;
+	u32 flags;
+	struct asr_vco_params *params;
+};
+
+#define to_clk_vco(vco_hw) container_of(vco_hw, struct clk_vco, hw)
+
+/**
+ * VCO Flags:
+ */
+#define ASR_PLL_SSC_FEAT	BIT(0)
+#define ASR_PLL_SSC_AON		BIT(1)
+/*
+ * For specific pll such as pll3p, don't check default rate to support
+ * more boot up op.
+ */
+#define ASR_PLL_SKIP_DEF_RATE	BIT(2)
+#define ASR_PLL_FRAC_FEAT	BIT(3)
+#define ASR_PLL_REG_NEW		BIT(4)
+#define ASR_PLL_FRAC_AUDIO	BIT(5)
+#define ASR_PLL_REG_DDR		BIT(6)
+
+extern struct clk *asr_clk_register_vco(const char *name,
+					   const char *parent_name,
+					   unsigned long flags, u32 vco_flags,
+					   spinlock_t *lock,
+					   struct asr_vco_params *params);
+
+#endif
diff --git a/drivers/clk/asr/clk.c b/drivers/clk/asr/clk.c
new file mode 100644
index 0000000..6948fc2
--- /dev/null
+++ b/drivers/clk/asr/clk.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * asr clock operation source file
+ *
+ * Copyright (C) 2019 ASR Microelectronics(Shanghai) Co., Ltd.
+ * Gang Wu <gangwu@...micro.com>
+ * Qiao Zhou <qiaozhou@...micro.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/of_address.h>
+
+#include "clk.h"
+
+void asr_clks_enable(char **clk_table, int clk_table_size)
+{
+	int i;
+	struct clk *clk;
+
+	for (i = 0; i < clk_table_size; i++) {
+		clk = __clk_lookup(clk_table[i]);
+		if (!IS_ERR_OR_NULL(clk))
+			clk_prepare_enable(clk);
+		else
+			pr_err("%s : can't find clk %s\n", __func__,
+				clk_table[i]);
+	}
+}
+
+int asr_clk_init(struct device_node *np, struct asr_clk_unit *unit,
+		int nr_clks)
+{
+	static struct clk **clk_table;
+
+	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
+	if (!clk_table)
+		return -ENOMEM;
+
+	unit->clk_table = clk_table;
+	unit->nr_clks = nr_clks;
+	unit->clk_data.clks = clk_table;
+	unit->clk_data.clk_num = nr_clks;
+	of_clk_add_provider(np, of_clk_src_onecell_get, &unit->clk_data);
+
+	return 0;
+}
+
+void asr_register_fixed_rate_clks(struct asr_clk_unit *unit,
+				struct asr_param_fixed_rate_clk *clks,
+				int size)
+{
+	int i;
+	struct clk *clk;
+
+	for (i = 0; i < size; i++) {
+		clk = clk_register_fixed_rate(NULL, clks[i].name,
+					clks[i].parent_name,
+					clks[i].flags,
+					clks[i].fixed_rate);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+		if (clks[i].id)
+			unit->clk_table[clks[i].id] = clk;
+	}
+}
+
+void asr_register_fixed_factor_clks(struct asr_clk_unit *unit,
+				struct asr_param_fixed_factor_clk *clks,
+				int size)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		clk = clk_register_fixed_factor(NULL, clks[i].name,
+						clks[i].parent_name,
+						clks[i].flags, clks[i].mult,
+						clks[i].div);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+		if (clks[i].id)
+			unit->clk_table[clks[i].id] = clk;
+	}
+}
+
+void asr_register_general_gate_clks(struct asr_clk_unit *unit,
+				struct asr_param_general_gate_clk *clks,
+				void __iomem *base, int size)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		clk = clk_register_gate(NULL, clks[i].name,
+					clks[i].parent_name,
+					clks[i].flags,
+					base + clks[i].offset,
+					clks[i].bit_idx,
+					clks[i].gate_flags,
+					clks[i].lock);
+
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+		if (clks[i].id)
+			unit->clk_table[clks[i].id] = clk;
+	}
+}
+
+void asr_register_gate_clks(struct asr_clk_unit *unit,
+			struct asr_param_gate_clk *clks,
+			void __iomem *base, int size)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		clk = asr_clk_register_gate(NULL, clks[i].name,
+					clks[i].parent_name,
+					clks[i].flags,
+					base + clks[i].offset,
+					clks[i].mask,
+					clks[i].val_enable,
+					clks[i].val_disable,
+					clks[i].gate_flags,
+					clks[i].lock);
+
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+		if (clks[i].id)
+			unit->clk_table[clks[i].id] = clk;
+	}
+}
+
+void asr_register_mux_clks(struct asr_clk_unit *unit,
+			struct asr_param_mux_clk *clks,
+			void __iomem *base, int size)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		clk = clk_register_mux(NULL, clks[i].name,
+					clks[i].parent_name,
+					clks[i].num_parents,
+					clks[i].flags,
+					base + clks[i].offset,
+					clks[i].shift,
+					clks[i].width,
+					clks[i].mux_flags,
+					clks[i].lock);
+
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+		if (clks[i].id)
+			unit->clk_table[clks[i].id] = clk;
+	}
+}
+
+void asr_register_div_clks(struct asr_clk_unit *unit,
+			struct asr_param_div_clk *clks,
+			void __iomem *base, int size)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		clk = clk_register_divider(NULL, clks[i].name,
+					clks[i].parent_name,
+					clks[i].flags,
+					base + clks[i].offset,
+					clks[i].shift,
+					clks[i].width,
+					clks[i].div_flags,
+					clks[i].lock);
+
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			continue;
+		}
+		if (clks[i].id)
+			unit->clk_table[clks[i].id] = clk;
+	}
+}
+
+void asr_clk_add(struct asr_clk_unit *unit, unsigned int id,
+			struct clk *clk)
+{
+	if (IS_ERR_OR_NULL(clk)) {
+		pr_err("CLK %d has invalid pointer %p\n", id, clk);
+		return;
+	}
+	if (id > unit->nr_clks) {
+		pr_err("CLK %d is invalid\n", id);
+		return;
+	}
+
+	unit->clk_table[id] = clk;
+}
diff --git a/drivers/clk/asr/clk.h b/drivers/clk/asr/clk.h
new file mode 100644
index 0000000..abe39cc
--- /dev/null
+++ b/drivers/clk/asr/clk.h
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * asr clock source file
+ *
+ * Copyright (C) 2019 ASR Microelectronics(Shanghai) Co., Ltd.
+ * Gang Wu <gangwu@...micro.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ASR_CLK_H__
+#define __ASR_CLK_H__
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#define APBC_NO_BUS_CTRL	BIT(0)
+#define APBC_POWER_CTRL		BIT(1)
+
+#define MHZ			(1000000UL)
+#define KHZ_TO_HZ		(1000)
+#define MHZ_TO_KHZ		(1000)
+
+/* Clock type "gate". ASR private gate */
+#define ASR_CLK_GATE_NEED_DELAY		BIT(0)
+
+/* Clock type "mix" */
+#define ASR_CLK_BITS_MASK(width, shift)			\
+		(((1 << (width)) - 1) << (shift))
+#define ASR_CLK_BITS_GET_VAL(data, width, shift)	\
+		((data & ASR_CLK_BITS_MASK(width, shift)) >> (shift))
+#define ASR_CLK_BITS_SET_VAL(val, width, shift)		\
+		(((val) << (shift)) & ASR_CLK_BITS_MASK(width, shift))
+
+enum {
+	ASR_CLK_MIX_TYPE_1REG_NOFC_V1,
+	ASR_CLK_MIX_TYPE_1REG_FC_V2,
+	ASR_CLK_MIX_TYPE_2REG_NOFC_V3,
+	ASR_CLK_MIX_TYPE_2REG_FC_V4,
+};
+
+/* The register layout */
+struct asr_clk_mix_reg_info {
+	void __iomem *reg_clk_ctrl;
+	void __iomem *reg_clk_sel;
+	void __iomem *reg_clk_xtc;
+	u8 width_div;
+	u8 shift_div;
+	u8 width_mux;
+	u8 shift_mux;
+	u32 bit_fc;
+};
+
+/* The suggested clock table from user. */
+struct asr_clk_mix_clk_table {
+	unsigned long rate;
+	u8 parent_index;
+	unsigned int divisor;
+	unsigned int valid;
+	unsigned int xtc;
+};
+
+struct asr_clk_mix_config {
+	struct asr_clk_mix_reg_info reg_info;
+	struct asr_clk_mix_clk_table *table;
+	unsigned int table_size;
+	struct clk_div_table *div_table;
+	unsigned long div_flags;
+	unsigned long mux_flags;
+};
+
+struct asr_clk_mix {
+	struct clk_hw hw;
+	struct asr_clk_mix_reg_info reg_info;
+	struct asr_clk_mix_clk_table *table;
+	struct clk_div_table *div_table;
+	unsigned int table_size;
+	unsigned long div_flags;
+	unsigned long mux_flags;
+	spinlock_t *lock;
+};
+
+extern struct clk *asr_clk_register_mix(struct device *dev,
+					const char *name,
+					const char **parent_names,
+					u8 num_parents,
+					unsigned long flags,
+					struct asr_clk_mix_config *config,
+					spinlock_t *lock);
+
+struct asr_clk_gate {
+	struct clk_hw hw;
+	void __iomem *reg;
+	u32 mask;
+	u32 val_enable;
+	u32 val_disable;
+	unsigned long flags;
+	spinlock_t *lock;
+};
+
+extern const struct clk_ops asr_clk_gate_ops;
+extern struct clk *asr_clk_register_gate(struct device *dev, const char *name,
+			const char *parent_name, unsigned long flags,
+			void __iomem *reg, u32 mask, u32 val_enable,
+			u32 val_disable, unsigned long gate_flags,
+			spinlock_t *lock);
+
+
+extern struct clk *asr_clk_register_pll2(const char *name,
+		const char *parent_name, unsigned long flags);
+extern struct clk *asr_clk_register_apbc(const char *name,
+		const char *parent_name, void __iomem *base,
+		unsigned int delay, unsigned long apbc_flags, spinlock_t *lock);
+extern struct clk *asr_clk_register_apmu(const char *name,
+		const char *parent_name, void __iomem *base, u32 enable_mask,
+		spinlock_t *lock);
+
+struct asr_clk_unit {
+	unsigned int nr_clks;
+	struct clk **clk_table;
+	struct clk_onecell_data clk_data;
+};
+
+struct asr_clk_data {
+	struct asr_clk_unit unit;
+	void __iomem *mpmu_base;
+	void __iomem *apmu_base;
+	void __iomem *apbc_base;
+	void __iomem *apbs_base;
+	void __iomem *ciu_base;
+	void __iomem *dciu_base;
+	void __iomem *ddrc_base;
+};
+
+struct asr_param_fixed_rate_clk {
+	unsigned int id;
+	char *name;
+	const char *parent_name;
+	unsigned long flags;
+	unsigned long fixed_rate;
+};
+void asr_register_fixed_rate_clks(struct asr_clk_unit *unit,
+				struct asr_param_fixed_rate_clk *clks,
+				int size);
+
+struct asr_param_fixed_factor_clk {
+	unsigned int id;
+	char *name;
+	const char *parent_name;
+	unsigned long mult;
+	unsigned long div;
+	unsigned long flags;
+};
+void asr_register_fixed_factor_clks(struct asr_clk_unit *unit,
+				struct asr_param_fixed_factor_clk *clks,
+				int size);
+
+struct asr_param_general_gate_clk {
+	unsigned int id;
+	const char *name;
+	const char *parent_name;
+	unsigned long offset;
+	u8 bit_idx;
+	unsigned long gate_flags;
+	spinlock_t *lock;
+	unsigned long flags;
+};
+void asr_register_general_gate_clks(struct asr_clk_unit *unit,
+				struct asr_param_general_gate_clk *clks,
+				void __iomem *base, int size);
+
+struct asr_param_gate_clk {
+	unsigned int id;
+	char *name;
+	const char *parent_name;
+	unsigned long flags;
+	unsigned long offset;
+	u32 mask;
+	u32 val_enable;
+	u32 val_disable;
+	unsigned long gate_flags;
+	spinlock_t *lock;
+};
+void asr_register_gate_clks(struct asr_clk_unit *unit,
+			struct asr_param_gate_clk *clks,
+			void __iomem *base, int size);
+
+struct asr_param_mux_clk {
+	unsigned int id;
+	char *name;
+	const char * const *parent_name;
+	u8 num_parents;
+	unsigned long flags;
+	unsigned long offset;
+	u8 shift;
+	u8 width;
+	unsigned long mux_flags;
+	spinlock_t *lock;
+};
+void asr_register_mux_clks(struct asr_clk_unit *unit,
+			struct asr_param_mux_clk *clks,
+			void __iomem *base, int size);
+
+struct asr_param_div_clk {
+	unsigned int id;
+	char *name;
+	const char *parent_name;
+	unsigned long flags;
+	unsigned long offset;
+	u8 shift;
+	u8 width;
+	unsigned long div_flags;
+	spinlock_t *lock;
+};
+void asr_register_div_clks(struct asr_clk_unit *unit,
+			struct asr_param_div_clk *clks,
+			void __iomem *base, int size);
+
+#define DEFINE_MIX_REG_INFO(w_d, s_d, w_m, s_m, fc)	\
+{									\
+	.width_div = (w_d),				\
+	.shift_div = (s_d),				\
+	.width_mux = (w_m),				\
+	.shift_mux = (s_m),				\
+	.bit_fc = (fc),					\
+}
+
+int asr_clk_init(struct device_node *np, struct asr_clk_unit *unit,
+		int nr_clks);
+void asr_clk_add(struct asr_clk_unit *unit, unsigned int id,
+		struct clk *clk);
+void asr_clks_enable(char **clk_table, int clk_table_size);
+#endif
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ