summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig21
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/axs10x/i2s_pll_clock.c1
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c303
-rw-r--r--drivers/clk/clk-cdce925.c108
-rw-r--r--drivers/clk/clk-conf.c15
-rw-r--r--drivers/clk/clk-cs2000-cp.c22
-rw-r--r--drivers/clk/clk-scpi.c14
-rw-r--r--drivers/clk/clk-stm32f4.c876
-rw-r--r--drivers/clk/clk-versaclock5.c791
-rw-r--r--drivers/clk/clk-wm831x.c3
-rw-r--r--drivers/clk/hisilicon/Kconfig7
-rw-r--r--drivers/clk/hisilicon/Makefile1
-rw-r--r--drivers/clk/hisilicon/clk-hi3660.c567
-rw-r--r--drivers/clk/hisilicon/clkgate-separated.c1
-rw-r--r--drivers/clk/imx/clk-imx6q.c21
-rw-r--r--drivers/clk/imx/clk-imx7d.c1
-rw-r--r--drivers/clk/imx/clk-pllv3.c99
-rw-r--r--drivers/clk/imx/clk-vf610.c4
-rw-r--r--drivers/clk/imx/clk.h1
-rw-r--r--drivers/clk/mediatek/Kconfig19
-rw-r--r--drivers/clk/meson/meson8b.c1
-rw-r--r--drivers/clk/mvebu/ap806-system-controller.c28
-rw-r--r--drivers/clk/mvebu/armada-xp.c39
-rw-r--r--drivers/clk/mvebu/clk-corediv.c23
-rw-r--r--drivers/clk/mvebu/clk-cpu.c8
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c71
-rw-r--r--drivers/clk/qcom/gcc-ipq4019.c479
-rw-r--r--drivers/clk/qcom/gcc-mdm9615.c30
-rw-r--r--drivers/clk/qcom/gcc-msm8994.c18
-rw-r--r--drivers/clk/qcom/gcc-msm8996.c1
-rw-r--r--drivers/clk/qcom/gdsc.c58
-rw-r--r--drivers/clk/renesas/clk-mstp.c38
-rw-r--r--drivers/clk/renesas/r8a7795-cpg-mssr.c1
-rw-r--r--drivers/clk/renesas/r8a7796-cpg-mssr.c10
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.c149
-rw-r--r--drivers/clk/samsung/Makefile1
-rw-r--r--drivers/clk/samsung/clk-exynos-audss.c24
-rw-r--r--drivers/clk/samsung/clk-exynos4.c4
-rw-r--r--drivers/clk/samsung/clk-exynos4415.c1022
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c14
-rw-r--r--drivers/clk/samsung/clk-exynos5433.c44
-rw-r--r--drivers/clk/samsung/clk-pll.c45
-rw-r--r--drivers/clk/samsung/clk-s3c2410.c4
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c4
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c4
-rw-r--r--drivers/clk/samsung/clk-s3c64xx.c4
-rw-r--r--drivers/clk/sunxi-ng/Kconfig31
-rw-r--r--drivers/clk/sunxi-ng/Makefile5
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun5i.c1022
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun5i.h67
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun6i-a31.c4
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a33.c16
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-h3.c10
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-v3s.c591
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-v3s.h63
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c283
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h33
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c144
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h25
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80.c1223
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80.h57
-rw-r--r--drivers/clk/sunxi-ng/ccu_common.c9
-rw-r--r--drivers/clk/sunxi-ng/ccu_common.h4
-rw-r--r--drivers/clk/sunxi-ng/ccu_div.c12
-rw-r--r--drivers/clk/sunxi-ng/ccu_div.h10
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.c10
-rw-r--r--drivers/clk/sunxi-ng/ccu_mult.c30
-rw-r--r--drivers/clk/sunxi-ng/ccu_mult.h24
-rw-r--r--drivers/clk/sunxi-ng/ccu_mux.c43
-rw-r--r--drivers/clk/sunxi-ng/ccu_nk.c22
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkm.c26
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkmp.c25
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.c17
-rw-r--r--drivers/clk/ti/divider.c31
-rw-r--r--drivers/clk/uniphier/clk-uniphier-core.c7
-rw-r--r--drivers/clk/uniphier/clk-uniphier-cpugear.c1
-rw-r--r--drivers/clk/uniphier/clk-uniphier-sys.c21
-rw-r--r--drivers/clk/ux500/abx500-clk.c44
-rw-r--r--drivers/clk/ux500/u8500_of_clk.c3
-rw-r--r--drivers/clk/x86/Makefile1
-rw-r--r--drivers/clk/x86/clk-pmc-atom.c371
-rw-r--r--drivers/clk/zte/clk-zx296718.c154
-rw-r--r--drivers/clk/zte/clk.c127
-rw-r--r--drivers/clk/zte/clk.h21
85 files changed, 8225 insertions, 1364 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 56c1998ced3e..9356ab4b7d76 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -95,16 +95,17 @@ config COMMON_CLK_CDCE706
This driver supports TI CDCE706 programmable 3-PLL clock synthesizer.
config COMMON_CLK_CDCE925
- tristate "Clock driver for TI CDCE925 devices"
+ tristate "Clock driver for TI CDCE913/925/937/949 devices"
depends on I2C
depends on OF
select REGMAP_I2C
help
---help---
- This driver supports the TI CDCE925 programmable clock synthesizer.
- The chip contains two PLLs with spread-spectrum clocking support and
- five output dividers. The driver only supports the following setup,
- and uses a fixed setting for the output muxes.
+ This driver supports the TI CDCE913/925/937/949 programmable clock
+ synthesizer. Each chip has different number of PLLs and outputs.
+ For example, the CDCE925 contains two PLLs with spread-spectrum
+ clocking support and five output dividers. The driver only supports
+ the following setup, and uses a fixed setting for the output muxes.
Y1 is derived from the input clock
Y2 and Y3 derive from PLL1
Y4 and Y5 derive from PLL2
@@ -198,6 +199,16 @@ config COMMON_CLK_OXNAS
---help---
Support for the OXNAS SoC Family clocks.
+config COMMON_CLK_VC5
+ tristate "Clock driver for IDT VersaClock5 devices"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
+ help
+ ---help---
+ This driver supports the IDT VersaClock5 programmable clock
+ generator.
+
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/mediatek/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 925081ec14c0..92c12b86c2e8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
+obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
@@ -87,6 +88,8 @@ obj-y += ti/
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
+ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_X86) += x86/
+endif
obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c
index 411310d29581..02d3bcd6216c 100644
--- a/drivers/clk/axs10x/i2s_pll_clock.c
+++ b/drivers/clk/axs10x/i2s_pll_clock.c
@@ -182,6 +182,7 @@ static int i2s_pll_clk_probe(struct platform_device *pdev)
if (IS_ERR(pll_clk->base))
return PTR_ERR(pll_clk->base);
+ memset(&init, 0, sizeof(init));
clk_name = node->name;
init.name = clk_name;
init.ops = &i2s_pll_ops;
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 0d14409097e7..025853870619 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -39,6 +39,7 @@
#include <linux/clk.h>
#include <linux/clk/bcm2835.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -98,7 +99,8 @@
#define CM_SMIDIV 0x0b4
/* no definition for 0x0b8 and 0x0bc */
#define CM_TCNTCTL 0x0c0
-#define CM_TCNTDIV 0x0c4
+# define CM_TCNT_SRC1_SHIFT 12
+#define CM_TCNTCNT 0x0c4
#define CM_TECCTL 0x0c8
#define CM_TECDIV 0x0cc
#define CM_TD0CTL 0x0d0
@@ -297,11 +299,32 @@
#define LOCK_TIMEOUT_NS 100000000
#define BCM2835_MAX_FB_RATE 1750000000u
+/*
+ * Names of clocks used within the driver that need to be replaced
+ * with an external parent's name. This array is in the order that
+ * the clocks node in the DT references external clocks.
+ */
+static const char *const cprman_parent_names[] = {
+ "xosc",
+ "dsi0_byte",
+ "dsi0_ddr2",
+ "dsi0_ddr",
+ "dsi1_byte",
+ "dsi1_ddr2",
+ "dsi1_ddr",
+};
+
struct bcm2835_cprman {
struct device *dev;
void __iomem *regs;
spinlock_t regs_lock; /* spinlock for all clocks */
- const char *osc_name;
+
+ /*
+ * Real names of cprman clock parents looked up through
+ * of_clk_get_parent_name(), which will be used in the
+ * parent_names[] arrays for clock registration.
+ */
+ const char *real_parent_names[ARRAY_SIZE(cprman_parent_names)];
/* Must be last */
struct clk_hw_onecell_data onecell;
@@ -317,6 +340,61 @@ static inline u32 cprman_read(struct bcm2835_cprman *cprman, u32 reg)
return readl(cprman->regs + reg);
}
+/* Does a cycle of measuring a clock through the TCNT clock, which may
+ * source from many other clocks in the system.
+ */
+static unsigned long bcm2835_measure_tcnt_mux(struct bcm2835_cprman *cprman,
+ u32 tcnt_mux)
+{
+ u32 osccount = 19200; /* 1ms */
+ u32 count;
+ ktime_t timeout;
+
+ spin_lock(&cprman->regs_lock);
+
+ cprman_write(cprman, CM_TCNTCTL, CM_KILL);
+
+ cprman_write(cprman, CM_TCNTCTL,
+ (tcnt_mux & CM_SRC_MASK) |
+ (tcnt_mux >> CM_SRC_BITS) << CM_TCNT_SRC1_SHIFT);
+
+ cprman_write(cprman, CM_OSCCOUNT, osccount);
+
+ /* do a kind delay at the start */
+ mdelay(1);
+
+ /* Finish off whatever is left of OSCCOUNT */
+ timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
+ while (cprman_read(cprman, CM_OSCCOUNT)) {
+ if (ktime_after(ktime_get(), timeout)) {
+ dev_err(cprman->dev, "timeout waiting for OSCCOUNT\n");
+ count = 0;
+ goto out;
+ }
+ cpu_relax();
+ }
+
+ /* Wait for BUSY to clear. */
+ timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
+ while (cprman_read(cprman, CM_TCNTCTL) & CM_BUSY) {
+ if (ktime_after(ktime_get(), timeout)) {
+ dev_err(cprman->dev, "timeout waiting for !BUSY\n");
+ count = 0;
+ goto out;
+ }
+ cpu_relax();
+ }
+
+ count = cprman_read(cprman, CM_TCNTCNT);
+
+ cprman_write(cprman, CM_TCNTCTL, 0);
+
+out:
+ spin_unlock(&cprman->regs_lock);
+
+ return count * 1000;
+}
+
static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base,
struct debugfs_reg32 *regs, size_t nregs,
struct dentry *dentry)
@@ -428,6 +506,7 @@ struct bcm2835_pll_divider_data {
u32 load_mask;
u32 hold_mask;
u32 fixed_divider;
+ u32 flags;
};
struct bcm2835_clock_data {
@@ -451,6 +530,8 @@ struct bcm2835_clock_data {
bool is_vpu_clock;
bool is_mash_clock;
+
+ u32 tcnt_mux;
};
struct bcm2835_gate_data {
@@ -906,6 +987,9 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
const struct bcm2835_clock_data *data = clock->data;
u64 temp;
+ if (data->int_bits == 0 && data->frac_bits == 0)
+ return parent_rate;
+
/*
* The divisor is a 12.12 fixed point field, but only some of
* the bits are populated in any given clock.
@@ -929,7 +1013,12 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct bcm2835_cprman *cprman = clock->cprman;
const struct bcm2835_clock_data *data = clock->data;
- u32 div = cprman_read(cprman, data->div_reg);
+ u32 div;
+
+ if (data->int_bits == 0 && data->frac_bits == 0)
+ return parent_rate;
+
+ div = cprman_read(cprman, data->div_reg);
return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
}
@@ -978,6 +1067,17 @@ static int bcm2835_clock_on(struct clk_hw *hw)
CM_GATE);
spin_unlock(&cprman->regs_lock);
+ /* Debug code to measure the clock once it's turned on to see
+ * if it's ticking at the rate we expect.
+ */
+ if (data->tcnt_mux && false) {
+ dev_info(cprman->dev,
+ "clk %s: rate %ld, measure %ld\n",
+ data->name,
+ clk_hw_get_rate(hw),
+ bcm2835_measure_tcnt_mux(cprman, data->tcnt_mux));
+ }
+
return 0;
}
@@ -1208,7 +1308,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
memset(&init, 0, sizeof(init));
/* All of the PLLs derive from the external oscillator. */
- init.parent_names = &cprman->osc_name;
+ init.parent_names = &cprman->real_parent_names[0];
init.num_parents = 1;
init.name = data->name;
init.ops = &bcm2835_pll_clk_ops;
@@ -1252,7 +1352,7 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
init.num_parents = 1;
init.name = divider_name;
init.ops = &bcm2835_pll_divider_clk_ops;
- init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
+ init.flags = data->flags | CLK_IGNORE_UNUSED;
divider = devm_kzalloc(cprman->dev, sizeof(*divider), GFP_KERNEL);
if (!divider)
@@ -1294,18 +1394,22 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
struct bcm2835_clock *clock;
struct clk_init_data init;
const char *parents[1 << CM_SRC_BITS];
- size_t i;
+ size_t i, j;
int ret;
/*
- * Replace our "xosc" references with the oscillator's
- * actual name.
+ * Replace our strings referencing parent clocks with the
+ * actual clock-output-name of the parent.
*/
for (i = 0; i < data->num_mux_parents; i++) {
- if (strcmp(data->parents[i], "xosc") == 0)
- parents[i] = cprman->osc_name;
- else
- parents[i] = data->parents[i];
+ parents[i] = data->parents[i];
+
+ for (j = 0; j < ARRAY_SIZE(cprman_parent_names); j++) {
+ if (strcmp(parents[i], cprman_parent_names[j]) == 0) {
+ parents[i] = cprman->real_parent_names[j];
+ break;
+ }
+ }
}
memset(&init, 0, sizeof(init));
@@ -1432,6 +1536,47 @@ static const char *const bcm2835_clock_vpu_parents[] = {
__VA_ARGS__)
/*
+ * DSI parent clocks. The DSI byte/DDR/DDR2 clocks come from the DSI
+ * analog PHY. The _inv variants are generated internally to cprman,
+ * but we don't use them so they aren't hooked up.
+ */
+static const char *const bcm2835_clock_dsi0_parents[] = {
+ "gnd",
+ "xosc",
+ "testdebug0",
+ "testdebug1",
+ "dsi0_ddr",
+ "dsi0_ddr_inv",
+ "dsi0_ddr2",
+ "dsi0_ddr2_inv",
+ "dsi0_byte",
+ "dsi0_byte_inv",
+};
+
+static const char *const bcm2835_clock_dsi1_parents[] = {
+ "gnd",
+ "xosc",
+ "testdebug0",
+ "testdebug1",
+ "dsi1_ddr",
+ "dsi1_ddr_inv",
+ "dsi1_ddr2",
+ "dsi1_ddr2_inv",
+ "dsi1_byte",
+ "dsi1_byte_inv",
+};
+
+#define REGISTER_DSI0_CLK(...) REGISTER_CLK( \
+ .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi0_parents), \
+ .parents = bcm2835_clock_dsi0_parents, \
+ __VA_ARGS__)
+
+#define REGISTER_DSI1_CLK(...) REGISTER_CLK( \
+ .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi1_parents), \
+ .parents = bcm2835_clock_dsi1_parents, \
+ __VA_ARGS__)
+
+/*
* the real definition of all the pll, pll_dividers and clocks
* these make use of the above REGISTER_* macros
*/
@@ -1466,7 +1611,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLA_CORE,
.load_mask = CM_PLLA_LOADCORE,
.hold_mask = CM_PLLA_HOLDCORE,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLA_PER] = REGISTER_PLL_DIV(
.name = "plla_per",
.source_pll = "plla",
@@ -1474,7 +1620,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLA_PER,
.load_mask = CM_PLLA_LOADPER,
.hold_mask = CM_PLLA_HOLDPER,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLA_DSI0] = REGISTER_PLL_DIV(
.name = "plla_dsi0",
.source_pll = "plla",
@@ -1490,7 +1637,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLA_CCP2,
.load_mask = CM_PLLA_LOADCCP2,
.hold_mask = CM_PLLA_HOLDCCP2,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
/* PLLB is used for the ARM's clock. */
[BCM2835_PLLB] = REGISTER_PLL(
@@ -1514,7 +1662,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLB_ARM,
.load_mask = CM_PLLB_LOADARM,
.hold_mask = CM_PLLB_HOLDARM,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
/*
* PLLC is the core PLL, used to drive the core VPU clock.
@@ -1543,7 +1692,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLC_CORE0,
.load_mask = CM_PLLC_LOADCORE0,
.hold_mask = CM_PLLC_HOLDCORE0,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLC_CORE1] = REGISTER_PLL_DIV(
.name = "pllc_core1",
.source_pll = "pllc",
@@ -1551,7 +1701,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLC_CORE1,
.load_mask = CM_PLLC_LOADCORE1,
.hold_mask = CM_PLLC_HOLDCORE1,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLC_CORE2] = REGISTER_PLL_DIV(
.name = "pllc_core2",
.source_pll = "pllc",
@@ -1559,7 +1710,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLC_CORE2,
.load_mask = CM_PLLC_LOADCORE2,
.hold_mask = CM_PLLC_HOLDCORE2,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLC_PER] = REGISTER_PLL_DIV(
.name = "pllc_per",
.source_pll = "pllc",
@@ -1567,7 +1719,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLC_PER,
.load_mask = CM_PLLC_LOADPER,
.hold_mask = CM_PLLC_HOLDPER,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
/*
* PLLD is the display PLL, used to drive DSI display panels.
@@ -1596,7 +1749,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLD_CORE,
.load_mask = CM_PLLD_LOADCORE,
.hold_mask = CM_PLLD_HOLDCORE,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLD_PER] = REGISTER_PLL_DIV(
.name = "plld_per",
.source_pll = "plld",
@@ -1604,7 +1758,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLD_PER,
.load_mask = CM_PLLD_LOADPER,
.hold_mask = CM_PLLD_HOLDPER,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLD_DSI0] = REGISTER_PLL_DIV(
.name = "plld_dsi0",
.source_pll = "plld",
@@ -1649,7 +1804,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLH_RCAL,
.load_mask = CM_PLLH_LOADRCAL,
.hold_mask = 0,
- .fixed_divider = 10),
+ .fixed_divider = 10,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLH_AUX] = REGISTER_PLL_DIV(
.name = "pllh_aux",
.source_pll = "pllh",
@@ -1657,7 +1813,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLH_AUX,
.load_mask = CM_PLLH_LOADAUX,
.hold_mask = 0,
- .fixed_divider = 1),
+ .fixed_divider = 1,
+ .flags = CLK_SET_RATE_PARENT),
[BCM2835_PLLH_PIX] = REGISTER_PLL_DIV(
.name = "pllh_pix",
.source_pll = "pllh",
@@ -1665,7 +1822,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLH_PIX,
.load_mask = CM_PLLH_LOADPIX,
.hold_mask = 0,
- .fixed_divider = 10),
+ .fixed_divider = 10,
+ .flags = CLK_SET_RATE_PARENT),
/* the clocks */
@@ -1677,7 +1835,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_OTPCTL,
.div_reg = CM_OTPDIV,
.int_bits = 4,
- .frac_bits = 0),
+ .frac_bits = 0,
+ .tcnt_mux = 6),
/*
* Used for a 1Mhz clock for the system clocksource, and also used
* bythe watchdog timer and the camera pulse generator.
@@ -1711,13 +1870,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_H264CTL,
.div_reg = CM_H264DIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 1),
[BCM2835_CLOCK_ISP] = REGISTER_VPU_CLK(
.name = "isp",
.ctl_reg = CM_ISPCTL,
.div_reg = CM_ISPDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 2),
/*
* Secondary SDRAM clock. Used for low-voltage modes when the PLL
@@ -1728,13 +1889,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_SDCCTL,
.div_reg = CM_SDCDIV,
.int_bits = 6,
- .frac_bits = 0),
+ .frac_bits = 0,
+ .tcnt_mux = 3),
[BCM2835_CLOCK_V3D] = REGISTER_VPU_CLK(
.name = "v3d",
.ctl_reg = CM_V3DCTL,
.div_reg = CM_V3DDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 4),
/*
* VPU clock. This doesn't have an enable bit, since it drives
* the bus for everything else, and is special so it doesn't need
@@ -1748,7 +1911,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.int_bits = 12,
.frac_bits = 8,
.flags = CLK_IS_CRITICAL,
- .is_vpu_clock = true),
+ .is_vpu_clock = true,
+ .tcnt_mux = 5),
/* clocks with per parent mux */
[BCM2835_CLOCK_AVEO] = REGISTER_PER_CLK(
@@ -1756,19 +1920,22 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_AVEOCTL,
.div_reg = CM_AVEODIV,
.int_bits = 4,
- .frac_bits = 0),
+ .frac_bits = 0,
+ .tcnt_mux = 38),
[BCM2835_CLOCK_CAM0] = REGISTER_PER_CLK(
.name = "cam0",
.ctl_reg = CM_CAM0CTL,
.div_reg = CM_CAM0DIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 14),
[BCM2835_CLOCK_CAM1] = REGISTER_PER_CLK(
.name = "cam1",
.ctl_reg = CM_CAM1CTL,
.div_reg = CM_CAM1DIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 15),
[BCM2835_CLOCK_DFT] = REGISTER_PER_CLK(
.name = "dft",
.ctl_reg = CM_DFTCTL,
@@ -1780,7 +1947,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_DPICTL,
.div_reg = CM_DPIDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 17),
/* Arasan EMMC clock */
[BCM2835_CLOCK_EMMC] = REGISTER_PER_CLK(
@@ -1788,7 +1956,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_EMMCCTL,
.div_reg = CM_EMMCDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 39),
/* General purpose (GPIO) clocks */
[BCM2835_CLOCK_GP0] = REGISTER_PER_CLK(
@@ -1797,7 +1966,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_GP0DIV,
.int_bits = 12,
.frac_bits = 12,
- .is_mash_clock = true),
+ .is_mash_clock = true,
+ .tcnt_mux = 20),
[BCM2835_CLOCK_GP1] = REGISTER_PER_CLK(
.name = "gp1",
.ctl_reg = CM_GP1CTL,
@@ -1805,7 +1975,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.int_bits = 12,
.frac_bits = 12,
.flags = CLK_IS_CRITICAL,
- .is_mash_clock = true),
+ .is_mash_clock = true,
+ .tcnt_mux = 21),
[BCM2835_CLOCK_GP2] = REGISTER_PER_CLK(
.name = "gp2",
.ctl_reg = CM_GP2CTL,
@@ -1820,40 +1991,46 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_HSMCTL,
.div_reg = CM_HSMDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 22),
[BCM2835_CLOCK_PCM] = REGISTER_PER_CLK(
.name = "pcm",
.ctl_reg = CM_PCMCTL,
.div_reg = CM_PCMDIV,
.int_bits = 12,
.frac_bits = 12,
- .is_mash_clock = true),
+ .is_mash_clock = true,
+ .tcnt_mux = 23),
[BCM2835_CLOCK_PWM] = REGISTER_PER_CLK(
.name = "pwm",
.ctl_reg = CM_PWMCTL,
.div_reg = CM_PWMDIV,
.int_bits = 12,
.frac_bits = 12,
- .is_mash_clock = true),
+ .is_mash_clock = true,
+ .tcnt_mux = 24),
[BCM2835_CLOCK_SLIM] = REGISTER_PER_CLK(
.name = "slim",
.ctl_reg = CM_SLIMCTL,
.div_reg = CM_SLIMDIV,
.int_bits = 12,
.frac_bits = 12,
- .is_mash_clock = true),
+ .is_mash_clock = true,
+ .tcnt_mux = 25),
[BCM2835_CLOCK_SMI] = REGISTER_PER_CLK(
.name = "smi",
.ctl_reg = CM_SMICTL,
.div_reg = CM_SMIDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 27),
[BCM2835_CLOCK_UART] = REGISTER_PER_CLK(
.name = "uart",
.ctl_reg = CM_UARTCTL,
.div_reg = CM_UARTDIV,
.int_bits = 10,
- .frac_bits = 12),
+ .frac_bits = 12,
+ .tcnt_mux = 28),
/* TV encoder clock. Only operating frequency is 108Mhz. */
[BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(
@@ -1866,7 +2043,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
* Allow rate change propagation only on PLLH_AUX which is
* assigned index 7 in the parent array.
*/
- .set_rate_parent = BIT(7)),
+ .set_rate_parent = BIT(7),
+ .tcnt_mux = 29),
/* dsi clocks */
[BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK(
@@ -1874,13 +2052,29 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_DSI0ECTL,
.div_reg = CM_DSI0EDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 18),
[BCM2835_CLOCK_DSI1E] = REGISTER_PER_CLK(
.name = "dsi1e",
.ctl_reg = CM_DSI1ECTL,
.div_reg = CM_DSI1EDIV,
.int_bits = 4,
- .frac_bits = 8),
+ .frac_bits = 8,
+ .tcnt_mux = 19),
+ [BCM2835_CLOCK_DSI0P] = REGISTER_DSI0_CLK(
+ .name = "dsi0p",
+ .ctl_reg = CM_DSI0PCTL,
+ .div_reg = CM_DSI0PDIV,
+ .int_bits = 0,
+ .frac_bits = 0,
+ .tcnt_mux = 12),
+ [BCM2835_CLOCK_DSI1P] = REGISTER_DSI1_CLK(
+ .name = "dsi1p",
+ .ctl_reg = CM_DSI1PCTL,
+ .div_reg = CM_DSI1PDIV,
+ .int_bits = 0,
+ .frac_bits = 0,
+ .tcnt_mux = 13),
/* the gates */
@@ -1939,8 +2133,19 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
if (IS_ERR(cprman->regs))
return PTR_ERR(cprman->regs);
- cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0);
- if (!cprman->osc_name)
+ memcpy(cprman->real_parent_names, cprman_parent_names,
+ sizeof(cprman_parent_names));
+ of_clk_parent_fill(dev->of_node, cprman->real_parent_names,
+ ARRAY_SIZE(cprman_parent_names));
+
+ /*
+ * Make sure the external oscillator has been registered.
+ *
+ * The other (DSI) clocks are not present on older device
+ * trees, which we still need to support for backwards
+ * compatibility.
+ */
+ if (!cprman->real_parent_names[0])
return -ENODEV;
platform_set_drvdata(pdev, cprman);
diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c
index f793b2d9238c..c933be01c7db 100644
--- a/drivers/clk/clk-cdce925.c
+++ b/drivers/clk/clk-cdce925.c
@@ -1,8 +1,8 @@
/*
- * Driver for TI Dual PLL CDCE925 clock synthesizer
+ * Driver for TI Multi PLL CDCE913/925/937/949 clock synthesizer
*
- * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1
- * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve
+ * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1,
+ * Y4/Y5 to PLL2, and so on. PLL frequency is set on a first-come-first-serve
* basis. Clients can directly request any frequency that the chip can
* deliver using the standard clk framework. In addition, the device can
* be configured and activated via the devicetree.
@@ -19,11 +19,32 @@
#include <linux/slab.h>
#include <linux/gcd.h>
-/* The chip has 2 PLLs which can be routed through dividers to 5 outputs.
+/* Each chip has different number of PLLs and outputs, for example:
+ * The CECE925 has 2 PLLs which can be routed through dividers to 5 outputs.
* Model this as 2 PLL clocks which are parents to the outputs.
*/
-#define NUMBER_OF_PLLS 2
-#define NUMBER_OF_OUTPUTS 5
+
+enum {
+ CDCE913,
+ CDCE925,
+ CDCE937,
+ CDCE949,
+};
+
+struct clk_cdce925_chip_info {
+ int num_plls;
+ int num_outputs;
+};
+
+static const struct clk_cdce925_chip_info clk_cdce925_chip_info_tbl[] = {
+ [CDCE913] = { .num_plls = 1, .num_outputs = 3 },
+ [CDCE925] = { .num_plls = 2, .num_outputs = 5 },
+ [CDCE937] = { .num_plls = 3, .num_outputs = 7 },
+ [CDCE949] = { .num_plls = 4, .num_outputs = 9 },
+};
+
+#define MAX_NUMBER_OF_PLLS 4
+#define MAX_NUMBER_OF_OUTPUTS 9
#define CDCE925_REG_GLOBAL1 0x01
#define CDCE925_REG_Y1SPIPDIVH 0x02
@@ -43,7 +64,7 @@ struct clk_cdce925_output {
struct clk_hw hw;
struct clk_cdce925_chip *chip;
u8 index;
- u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */
+ u16 pdiv; /* 1..127 for Y2-Y9; 1..1023 for Y1 */
};
#define to_clk_cdce925_output(_hw) \
container_of(_hw, struct clk_cdce925_output, hw)
@@ -60,8 +81,9 @@ struct clk_cdce925_pll {
struct clk_cdce925_chip {
struct regmap *regmap;
struct i2c_client *i2c_client;
- struct clk_cdce925_pll pll[NUMBER_OF_PLLS];
- struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS];
+ const struct clk_cdce925_chip_info *chip_info;
+ struct clk_cdce925_pll pll[MAX_NUMBER_OF_PLLS];
+ struct clk_cdce925_output clk[MAX_NUMBER_OF_OUTPUTS];
};
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
@@ -284,6 +306,18 @@ static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv)
case 4:
regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv);
break;
+ case 5:
+ regmap_update_bits(data->chip->regmap, 0x36, 0x7F, pdiv);
+ break;
+ case 6:
+ regmap_update_bits(data->chip->regmap, 0x37, 0x7F, pdiv);
+ break;
+ case 7:
+ regmap_update_bits(data->chip->regmap, 0x46, 0x7F, pdiv);
+ break;
+ case 8:
+ regmap_update_bits(data->chip->regmap, 0x47, 0x7F, pdiv);
+ break;
}
}
@@ -302,6 +336,14 @@ static void cdce925_clk_activate(struct clk_cdce925_output *data)
case 4:
regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03);
break;
+ case 5:
+ case 6:
+ regmap_update_bits(data->chip->regmap, 0x34, 0x03, 0x03);
+ break;
+ case 7:
+ case 8:
+ regmap_update_bits(data->chip->regmap, 0x44, 0x03, 0x03);
+ break;
}
}
@@ -474,15 +516,6 @@ static const struct clk_ops cdce925_clk_y1_ops = {
.set_rate = cdce925_clk_y1_set_rate,
};
-
-static struct regmap_config cdce925_regmap_config = {
- .name = "configuration0",
- .reg_bits = 8,
- .val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
- .max_register = 0x2F,
-};
-
#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00
#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80
@@ -582,13 +615,19 @@ static int cdce925_probe(struct i2c_client *client,
struct clk_cdce925_chip *data;
struct device_node *node = client->dev.of_node;
const char *parent_name;
- const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,};
+ const char *pll_clk_name[MAX_NUMBER_OF_PLLS] = {NULL,};
struct clk_init_data init;
u32 value;
int i;
int err;
struct device_node *np_output;
char child_name[6];
+ struct regmap_config config = {
+ .name = "configuration0",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ };
dev_dbg(&client->dev, "%s\n", __func__);
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
@@ -596,8 +635,11 @@ static int cdce925_probe(struct i2c_client *client,
return -ENOMEM;
data->i2c_client = client;
+ data->chip_info = &clk_cdce925_chip_info_tbl[id->driver_data];
+ config.max_register = CDCE925_OFFSET_PLL +
+ data->chip_info->num_plls * 0x10 - 1;
data->regmap = devm_regmap_init(&client->dev, &regmap_cdce925_bus,
- &client->dev, &cdce925_regmap_config);
+ &client->dev, &config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "failed to allocate register map\n");
return PTR_ERR(data->regmap);
@@ -626,7 +668,7 @@ static int cdce925_probe(struct i2c_client *client,
init.num_parents = parent_name ? 1 : 0;
/* Register PLL clocks */
- for (i = 0; i < NUMBER_OF_PLLS; ++i) {
+ for (i = 0; i < data->chip_info->num_plls; ++i) {
pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d",
client->dev.of_node->name, i);
init.name = pll_clk_name[i];
@@ -684,7 +726,7 @@ static int cdce925_probe(struct i2c_client *client,
init.ops = &cdce925_clk_ops;
init.flags = CLK_SET_RATE_PARENT;
init.num_parents = 1;
- for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) {
+ for (i = 1; i < data->chip_info->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%s.Y%d",
client->dev.of_node->name, i+1);
data->clk[i].chip = data;
@@ -702,6 +744,16 @@ static int cdce925_probe(struct i2c_client *client,
/* Mux Y4/5 to PLL2 */
init.parent_names = &pll_clk_name[1];
break;
+ case 5:
+ case 6:
+ /* Mux Y6/7 to PLL3 */
+ init.parent_names = &pll_clk_name[2];
+ break;
+ case 7:
+ case 8:
+ /* Mux Y8/9 to PLL4 */
+ init.parent_names = &pll_clk_name[3];
+ break;
}
err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
kfree(init.name); /* clock framework made a copy of the name */
@@ -720,7 +772,7 @@ static int cdce925_probe(struct i2c_client *client,
err = 0;
error:
- for (i = 0; i < NUMBER_OF_PLLS; ++i)
+ for (i = 0; i < data->chip_info->num_plls; ++i)
/* clock framework made a copy of the name */
kfree(pll_clk_name[i]);
@@ -728,13 +780,19 @@ error:
}
static const struct i2c_device_id cdce925_id[] = {
- { "cdce925", 0 },
+ { "cdce913", CDCE913 },
+ { "cdce925", CDCE925 },
+ { "cdce937", CDCE937 },
+ { "cdce949", CDCE949 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cdce925_id);
static const struct of_device_id clk_cdce925_of_match[] = {
+ { .compatible = "ti,cdce913" },
{ .compatible = "ti,cdce925" },
+ { .compatible = "ti,cdce937" },
+ { .compatible = "ti,cdce949" },
{ },
};
MODULE_DEVICE_TABLE(of, clk_cdce925_of_match);
@@ -750,5 +808,5 @@ static struct i2c_driver cdce925_driver = {
module_i2c_driver(cdce925_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
-MODULE_DESCRIPTION("cdce925 driver");
+MODULE_DESCRIPTION("TI CDCE913/925/937/949 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c
index 674785d968a3..e0e02a6e5900 100644
--- a/drivers/clk/clk-conf.c
+++ b/drivers/clk/clk-conf.c
@@ -40,8 +40,9 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
return 0;
pclk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(pclk)) {
- pr_warn("clk: couldn't get parent clock %d for %s\n",
- index, node->full_name);
+ if (PTR_ERR(pclk) != -EPROBE_DEFER)
+ pr_warn("clk: couldn't get parent clock %d for %s\n",
+ index, node->full_name);
return PTR_ERR(pclk);
}
@@ -55,8 +56,9 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
}
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
- pr_warn("clk: couldn't get assigned clock %d for %s\n",
- index, node->full_name);
+ if (PTR_ERR(clk) != -EPROBE_DEFER)
+ pr_warn("clk: couldn't get assigned clock %d for %s\n",
+ index, node->full_name);
rc = PTR_ERR(clk);
goto err;
}
@@ -99,8 +101,9 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
- pr_warn("clk: couldn't get clock %d for %s\n",
- index, node->full_name);
+ if (PTR_ERR(clk) != -EPROBE_DEFER)
+ pr_warn("clk: couldn't get clock %d for %s\n",
+ index, node->full_name);
return PTR_ERR(clk);
}
diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index 021f3daf34e1..3fca0526d940 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -59,6 +59,10 @@ struct cs2000_priv {
struct i2c_client *client;
struct clk *clk_in;
struct clk *ref_clk;
+
+ /* suspend/resume */
+ unsigned long saved_rate;
+ unsigned long saved_parent_rate;
};
static const struct of_device_id cs2000_of_match[] = {
@@ -286,6 +290,9 @@ static int __cs2000_set_rate(struct cs2000_priv *priv, int ch,
if (ret < 0)
return ret;
+ priv->saved_rate = rate;
+ priv->saved_parent_rate = parent_rate;
+
return 0;
}
@@ -489,9 +496,24 @@ probe_err:
return ret;
}
+static int cs2000_resume(struct device *dev)
+{
+ struct cs2000_priv *priv = dev_get_drvdata(dev);
+ int ch = 0; /* it uses ch0 only at this point */
+
+ return __cs2000_set_rate(priv, ch,
+ priv->saved_rate,
+ priv->saved_parent_rate);
+}
+
+static const struct dev_pm_ops cs2000_pm_ops = {
+ .resume_early = cs2000_resume,
+};
+
static struct i2c_driver cs2000_driver = {
.driver = {
.name = "cs2000-cp",
+ .pm = &cs2000_pm_ops,
.of_match_table = cs2000_of_match,
},
.probe = cs2000_probe,
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
index 2a3e9d8e88b0..96d37175d0ad 100644
--- a/drivers/clk/clk-scpi.c
+++ b/drivers/clk/clk-scpi.c
@@ -290,13 +290,15 @@ static int scpi_clocks_probe(struct platform_device *pdev)
of_node_put(child);
return ret;
}
- }
- /* Add the virtual cpufreq device */
- cpufreq_dev = platform_device_register_simple("scpi-cpufreq",
- -1, NULL, 0);
- if (IS_ERR(cpufreq_dev))
- pr_warn("unable to register cpufreq device");
+ if (match->data != &scpi_dvfs_ops)
+ continue;
+ /* Add the virtual cpufreq device if it's DVFS clock provider */
+ cpufreq_dev = platform_device_register_simple("scpi-cpufreq",
+ -1, NULL, 0);
+ if (IS_ERR(cpufreq_dev))
+ pr_warn("unable to register cpufreq device");
+ }
return 0;
}
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 5eb05dbf59b8..ab609a76706f 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -28,6 +28,14 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+/*
+ * Include list of clocks wich are not derived from system clock (SYSCLOCK)
+ * The index of these clocks is the secondary index of DT bindings
+ *
+ */
+#include <dt-bindings/clock/stm32fx-clock.h>
+
+#define STM32F4_RCC_CR 0x00
#define STM32F4_RCC_PLLCFGR 0x04
#define STM32F4_RCC_CFGR 0x08
#define STM32F4_RCC_AHB1ENR 0x30
@@ -37,6 +45,15 @@
#define STM32F4_RCC_APB2ENR 0x44
#define STM32F4_RCC_BDCR 0x70
#define STM32F4_RCC_CSR 0x74
+#define STM32F4_RCC_PLLI2SCFGR 0x84
+#define STM32F4_RCC_PLLSAICFGR 0x88
+#define STM32F4_RCC_DCKCFGR 0x8c
+#define STM32F7_RCC_DCKCFGR2 0x90
+
+#define NONE -1
+#define NO_IDX NONE
+#define NO_MUX NONE
+#define NO_GATE NONE
struct stm32f4_gate_data {
u8 offset;
@@ -195,7 +212,7 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
- { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" },
+ { STM32F4_RCC_APB2ENR, 11, "sdio", "sdmux" },
{ STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" },
@@ -208,7 +225,79 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
};
-enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK };
+static const struct stm32f4_gate_data stm32f746_gates[] __initconst = {
+ { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 20, "dtcmram", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" },
+ { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" },
+
+ { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" },
+ { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" },
+ { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" },
+ { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" },
+ { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" },
+
+ { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div",
+ CLK_IGNORE_UNUSED },
+ { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div",
+ CLK_IGNORE_UNUSED },
+
+ { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" },
+ { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 16, "spdifrx", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 27, "cec", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" },
+ { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" },
+
+ { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" },
+ { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" },
+ { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 11, "sdmmc", "sdmux" },
+ { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" },
+ { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" },
+ { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" },
+ { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 23, "sai2", "apb2_div" },
+ { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
+};
/*
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
@@ -224,6 +313,10 @@ static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
0x0000000000000003ull,
0x0c777f33f6fec9ffull };
+static const u64 stm32f746_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
+ 0x0000000000000003ull,
+ 0x04f77f033e01c9ffull };
+
static const u64 *stm32f4_gate_map;
static struct clk_hw **clks;
@@ -233,6 +326,8 @@ static void __iomem *base;
static struct regmap *pdrm;
+static int stm32fx_end_primary_clk;
+
/*
* "Multiplier" device for APBx clocks.
*
@@ -324,23 +419,342 @@ static struct clk *clk_register_apb_mul(struct device *dev, const char *name,
return clk;
}
-/*
- * Decode current PLL state and (statically) model the state we inherit from
- * the bootloader.
- */
-static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk)
+enum {
+ PLL,
+ PLL_I2S,
+ PLL_SAI,
+};
+
+static const struct clk_div_table pll_divp_table[] = {
+ { 0, 2 }, { 1, 4 }, { 2, 6 }, { 3, 8 }, { 0 }
+};
+
+static const struct clk_div_table pll_divr_table[] = {
+ { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 }
+};
+
+struct stm32f4_pll {
+ spinlock_t *lock;
+ struct clk_gate gate;
+ u8 offset;
+ u8 bit_rdy_idx;
+ u8 status;
+ u8 n_start;
+};
+
+#define to_stm32f4_pll(_gate) container_of(_gate, struct stm32f4_pll, gate)
+
+struct stm32f4_pll_post_div_data {
+ int idx;
+ u8 pll_num;
+ const char *name;
+ const char *parent;
+ u8 flag;
+ u8 offset;
+ u8 shift;
+ u8 width;
+ u8 flag_div;
+ const struct clk_div_table *div_table;
+};
+
+struct stm32f4_vco_data {
+ const char *vco_name;
+ u8 offset;
+ u8 bit_idx;
+ u8 bit_rdy_idx;
+};
+
+static const struct stm32f4_vco_data vco_data[] = {
+ { "vco", STM32F4_RCC_PLLCFGR, 24, 25 },
+ { "vco-i2s", STM32F4_RCC_PLLI2SCFGR, 26, 27 },
+ { "vco-sai", STM32F4_RCC_PLLSAICFGR, 28, 29 },
+};
+
+
+static const struct clk_div_table post_divr_table[] = {
+ { 0, 2 }, { 1, 4 }, { 2, 8 }, { 3, 16 }, { 0 }
+};
+
+#define MAX_POST_DIV 3
+static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = {
+ { CLK_I2SQ_PDIV, PLL_I2S, "plli2s-q-div", "plli2s-q",
+ CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL},
+
+ { CLK_SAIQ_PDIV, PLL_SAI, "pllsai-q-div", "pllsai-q",
+ CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL },
+
+ { NO_IDX, PLL_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
+ STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table },
+};
+
+struct stm32f4_div_data {
+ u8 shift;
+ u8 width;
+ u8 flag_div;
+ const struct clk_div_table *div_table;
+};
+
+#define MAX_PLL_DIV 3
+static const struct stm32f4_div_data div_data[MAX_PLL_DIV] = {
+ { 16, 2, 0, pll_divp_table },
+ { 24, 4, CLK_DIVIDER_ONE_BASED, NULL },
+ { 28, 3, 0, pll_divr_table },
+};
+
+struct stm32f4_pll_data {
+ u8 pll_num;
+ u8 n_start;
+ const char *div_name[MAX_PLL_DIV];
+};
+
+static const struct stm32f4_pll_data stm32f429_pll[MAX_PLL_DIV] = {
+ { PLL, 192, { "pll", "pll48", NULL } },
+ { PLL_I2S, 192, { NULL, "plli2s-q", "plli2s-r" } },
+ { PLL_SAI, 49, { NULL, "pllsai-q", "pllsai-r" } },
+};
+
+static const struct stm32f4_pll_data stm32f469_pll[MAX_PLL_DIV] = {
+ { PLL, 50, { "pll", "pll-q", NULL } },
+ { PLL_I2S, 50, { "plli2s-p", "plli2s-q", "plli2s-r" } },
+ { PLL_SAI, 50, { "pllsai-p", "pllsai-q", "pllsai-r" } },
+};
+
+static int stm32f4_pll_is_enabled(struct clk_hw *hw)
+{
+ return clk_gate_ops.is_enabled(hw);
+}
+
+static int stm32f4_pll_enable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+ int ret = 0;
+ unsigned long reg;
+
+ ret = clk_gate_ops.enable(hw);
+
+ ret = readl_relaxed_poll_timeout_atomic(base + STM32F4_RCC_CR, reg,
+ reg & (1 << pll->bit_rdy_idx), 0, 10000);
+
+ return ret;
+}
+
+static void stm32f4_pll_disable(struct clk_hw *hw)
+{
+ clk_gate_ops.disable(hw);
+}
+
+static unsigned long stm32f4_pll_recalc(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+ unsigned long n;
+
+ n = (readl(base + pll->offset) >> 6) & 0x1ff;
+
+ return parent_rate * n;
+}
+
+static long stm32f4_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+ unsigned long n;
+
+ n = rate / *prate;
+
+ if (n < pll->n_start)
+ n = pll->n_start;
+ else if (n > 432)
+ n = 432;
+
+ return *prate * n;
+}
+
+static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+
+ unsigned long n;
+ unsigned long val;
+ int pll_state;
+
+ pll_state = stm32f4_pll_is_enabled(hw);
+
+ if (pll_state)
+ stm32f4_pll_disable(hw);
+
+ n = rate / parent_rate;
+
+ val = readl(base + pll->offset) & ~(0x1ff << 6);
+
+ writel(val | ((n & 0x1ff) << 6), base + pll->offset);
+
+ if (pll_state)
+ stm32f4_pll_enable(hw);
+
+ return 0;
+}
+
+static const struct clk_ops stm32f4_pll_gate_ops = {
+ .enable = stm32f4_pll_enable,
+ .disable = stm32f4_pll_disable,
+ .is_enabled = stm32f4_pll_is_enabled,
+ .recalc_rate = stm32f4_pll_recalc,
+ .round_rate = stm32f4_pll_round_rate,
+ .set_rate = stm32f4_pll_set_rate,
+};
+
+struct stm32f4_pll_div {
+ struct clk_divider div;
+ struct clk_hw *hw_pll;
+};
+
+#define to_pll_div_clk(_div) container_of(_div, struct stm32f4_pll_div, div)
+
+static unsigned long stm32f4_pll_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return clk_divider_ops.recalc_rate(hw, parent_rate);
+}
+
+static long stm32f4_pll_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ return clk_divider_ops.round_rate(hw, rate, prate);
+}
+
+static int stm32f4_pll_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR);
+ int pll_state, ret;
+
+ struct clk_divider *div = to_clk_divider(hw);
+ struct stm32f4_pll_div *pll_div = to_pll_div_clk(div);
+
+ pll_state = stm32f4_pll_is_enabled(pll_div->hw_pll);
+
+ if (pll_state)
+ stm32f4_pll_disable(pll_div->hw_pll);
+
+ ret = clk_divider_ops.set_rate(hw, rate, parent_rate);
- unsigned long pllm = pllcfgr & 0x3f;
- unsigned long plln = (pllcfgr >> 6) & 0x1ff;
- unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1);
- const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk;
- unsigned long pllq = (pllcfgr >> 24) & 0xf;
+ if (pll_state)
+ stm32f4_pll_enable(pll_div->hw_pll);
- clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm);
- clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp);
- clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq);
+ return ret;
+}
+
+static const struct clk_ops stm32f4_pll_div_ops = {
+ .recalc_rate = stm32f4_pll_div_recalc_rate,
+ .round_rate = stm32f4_pll_div_round_rate,
+ .set_rate = stm32f4_pll_div_set_rate,
+};
+
+static struct clk_hw *clk_register_pll_div(const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ struct clk_hw *pll_hw, spinlock_t *lock)
+{
+ struct stm32f4_pll_div *pll_div;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ /* allocate the divider */
+ pll_div = kzalloc(sizeof(*pll_div), GFP_KERNEL);
+ if (!pll_div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &stm32f4_pll_div_ops;
+ init.flags = flags;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* struct clk_divider assignments */
+ pll_div->div.reg = reg;
+ pll_div->div.shift = shift;
+ pll_div->div.width = width;
+ pll_div->div.flags = clk_divider_flags;
+ pll_div->div.lock = lock;
+ pll_div->div.table = table;
+ pll_div->div.hw.init = &init;
+
+ pll_div->hw_pll = pll_hw;
+
+ /* register the clock */
+ hw = &pll_div->div.hw;
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ kfree(pll_div);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
+static struct clk_hw *stm32f4_rcc_register_pll(const char *pllsrc,
+ const struct stm32f4_pll_data *data, spinlock_t *lock)
+{
+ struct stm32f4_pll *pll;
+ struct clk_init_data init = { NULL };
+ void __iomem *reg;
+ struct clk_hw *pll_hw;
+ int ret;
+ int i;
+ const struct stm32f4_vco_data *vco;
+
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ vco = &vco_data[data->pll_num];
+
+ init.name = vco->vco_name;
+ init.ops = &stm32f4_pll_gate_ops;
+ init.flags = CLK_SET_RATE_GATE;
+ init.parent_names = &pllsrc;
+ init.num_parents = 1;
+
+ pll->gate.lock = lock;
+ pll->gate.reg = base + STM32F4_RCC_CR;
+ pll->gate.bit_idx = vco->bit_idx;
+ pll->gate.hw.init = &init;
+
+ pll->offset = vco->offset;
+ pll->n_start = data->n_start;
+ pll->bit_rdy_idx = vco->bit_rdy_idx;
+ pll->status = (readl(base + STM32F4_RCC_CR) >> vco->bit_idx) & 0x1;
+
+ reg = base + pll->offset;
+
+ pll_hw = &pll->gate.hw;
+ ret = clk_hw_register(NULL, pll_hw);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ for (i = 0; i < MAX_PLL_DIV; i++)
+ if (data->div_name[i])
+ clk_register_pll_div(data->div_name[i],
+ vco->vco_name,
+ 0,
+ reg,
+ div_data[i].shift,
+ div_data[i].width,
+ div_data[i].flag_div,
+ div_data[i].div_table,
+ pll_hw,
+ lock);
+ return pll_hw;
}
/*
@@ -352,7 +766,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
u64 table[MAX_GATE_MAP];
if (primary == 1) {
- if (WARN_ON(secondary >= END_PRIMARY_CLK))
+ if (WARN_ON(secondary >= stm32fx_end_primary_clk))
return -EINVAL;
return secondary;
}
@@ -369,7 +783,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
table[BIT_ULL_WORD(secondary)] &=
GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0);
- return END_PRIMARY_CLK - 1 + hweight64(table[0]) +
+ return stm32fx_end_primary_clk - 1 + hweight64(table[0]) +
(BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) +
(BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0);
}
@@ -611,22 +1025,291 @@ static const char *rtc_parents[4] = {
"no-clock", "lse", "lsi", "hse-rtc"
};
+static const char *lcd_parent[1] = { "pllsai-r-div" };
+
+static const char *i2s_parents[2] = { "plli2s-r", NULL };
+
+static const char *sai_parents[4] = { "pllsai-q-div", "plli2s-q-div", NULL,
+ "no-clock" };
+
+static const char *pll48_parents[2] = { "pll-q", "pllsai-p" };
+
+static const char *sdmux_parents[2] = { "pll48", "sys" };
+
+static const char *hdmi_parents[2] = { "lse", "hsi_div488" };
+
+static const char *spdif_parent[1] = { "plli2s-p" };
+
+static const char *lptim_parent[4] = { "apb1_mul", "lsi", "hsi", "lse" };
+
+static const char *uart_parents1[4] = { "apb2_div", "sys", "hsi", "lse" };
+static const char *uart_parents2[4] = { "apb1_div", "sys", "hsi", "lse" };
+
+static const char *i2c_parents[4] = { "apb1_div", "sys", "hsi", "no-clock" };
+
+struct stm32_aux_clk {
+ int idx;
+ const char *name;
+ const char * const *parent_names;
+ int num_parents;
+ int offset_mux;
+ u8 shift;
+ u8 mask;
+ int offset_gate;
+ u8 bit_idx;
+ unsigned long flags;
+};
+
struct stm32f4_clk_data {
const struct stm32f4_gate_data *gates_data;
const u64 *gates_map;
int gates_num;
+ const struct stm32f4_pll_data *pll_data;
+ const struct stm32_aux_clk *aux_clk;
+ int aux_clk_num;
+ int end_primary;
+};
+
+static const struct stm32_aux_clk stm32f429_aux_clk[] = {
+ {
+ CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent),
+ NO_MUX, 0, 0,
+ STM32F4_RCC_APB2ENR, 26,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents),
+ STM32F4_RCC_CFGR, 23, 1,
+ NO_GATE, 0,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 20, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 22, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+};
+
+static const struct stm32_aux_clk stm32f469_aux_clk[] = {
+ {
+ CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent),
+ NO_MUX, 0, 0,
+ STM32F4_RCC_APB2ENR, 26,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents),
+ STM32F4_RCC_CFGR, 23, 1,
+ NO_GATE, 0,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 20, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 22, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents),
+ STM32F4_RCC_DCKCFGR, 27, 1,
+ NO_GATE, 0,
+ 0
+ },
+ {
+ NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents),
+ STM32F4_RCC_DCKCFGR, 28, 1,
+ NO_GATE, 0,
+ 0
+ },
+};
+
+static const struct stm32_aux_clk stm32f746_aux_clk[] = {
+ {
+ CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent),
+ NO_MUX, 0, 0,
+ STM32F4_RCC_APB2ENR, 26,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents),
+ STM32F4_RCC_CFGR, 23, 1,
+ NO_GATE, 0,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI1, "sai1_clk", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 20, 3,
+ STM32F4_RCC_APB2ENR, 22,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_SAI2, "sai2_clk", sai_parents, ARRAY_SIZE(sai_parents),
+ STM32F4_RCC_DCKCFGR, 22, 3,
+ STM32F4_RCC_APB2ENR, 23,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents),
+ STM32F7_RCC_DCKCFGR2, 27, 1,
+ NO_GATE, 0,
+ 0
+ },
+ {
+ NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents),
+ STM32F7_RCC_DCKCFGR2, 28, 1,
+ NO_GATE, 0,
+ 0
+ },
+ {
+ CLK_HDMI_CEC, "hdmi-cec",
+ hdmi_parents, ARRAY_SIZE(hdmi_parents),
+ STM32F7_RCC_DCKCFGR2, 26, 1,
+ NO_GATE, 0,
+ 0
+ },
+ {
+ CLK_SPDIF, "spdif-rx",
+ spdif_parent, ARRAY_SIZE(spdif_parent),
+ STM32F7_RCC_DCKCFGR2, 22, 3,
+ STM32F4_RCC_APB2ENR, 23,
+ CLK_SET_RATE_PARENT
+ },
+ {
+ CLK_USART1, "usart1",
+ uart_parents1, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 0, 3,
+ STM32F4_RCC_APB2ENR, 4,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_USART2, "usart2",
+ uart_parents2, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 2, 3,
+ STM32F4_RCC_APB1ENR, 17,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_USART3, "usart3",
+ uart_parents2, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 4, 3,
+ STM32F4_RCC_APB1ENR, 18,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_UART4, "uart4",
+ uart_parents2, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 6, 3,
+ STM32F4_RCC_APB1ENR, 19,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_UART5, "uart5",
+ uart_parents2, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 8, 3,
+ STM32F4_RCC_APB1ENR, 20,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_USART6, "usart6",
+ uart_parents1, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 10, 3,
+ STM32F4_RCC_APB2ENR, 5,
+ CLK_SET_RATE_PARENT,
+ },
+
+ {
+ CLK_UART7, "uart7",
+ uart_parents2, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 12, 3,
+ STM32F4_RCC_APB1ENR, 30,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_UART8, "uart8",
+ uart_parents2, ARRAY_SIZE(uart_parents1),
+ STM32F7_RCC_DCKCFGR2, 14, 3,
+ STM32F4_RCC_APB1ENR, 31,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_I2C1, "i2c1",
+ i2c_parents, ARRAY_SIZE(i2c_parents),
+ STM32F7_RCC_DCKCFGR2, 16, 3,
+ STM32F4_RCC_APB1ENR, 21,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_I2C2, "i2c2",
+ i2c_parents, ARRAY_SIZE(i2c_parents),
+ STM32F7_RCC_DCKCFGR2, 18, 3,
+ STM32F4_RCC_APB1ENR, 22,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_I2C3, "i2c3",
+ i2c_parents, ARRAY_SIZE(i2c_parents),
+ STM32F7_RCC_DCKCFGR2, 20, 3,
+ STM32F4_RCC_APB1ENR, 23,
+ CLK_SET_RATE_PARENT,
+ },
+ {
+ CLK_I2C4, "i2c4",
+ i2c_parents, ARRAY_SIZE(i2c_parents),
+ STM32F7_RCC_DCKCFGR2, 22, 3,
+ STM32F4_RCC_APB1ENR, 24,
+ CLK_SET_RATE_PARENT,
+ },
+
+ {
+ CLK_LPTIMER, "lptim1",
+ lptim_parent, ARRAY_SIZE(lptim_parent),
+ STM32F7_RCC_DCKCFGR2, 24, 3,
+ STM32F4_RCC_APB1ENR, 9,
+ CLK_SET_RATE_PARENT
+ },
};
static const struct stm32f4_clk_data stm32f429_clk_data = {
+ .end_primary = END_PRIMARY_CLK,
.gates_data = stm32f429_gates,
.gates_map = stm32f42xx_gate_map,
.gates_num = ARRAY_SIZE(stm32f429_gates),
+ .pll_data = stm32f429_pll,
+ .aux_clk = stm32f429_aux_clk,
+ .aux_clk_num = ARRAY_SIZE(stm32f429_aux_clk),
};
static const struct stm32f4_clk_data stm32f469_clk_data = {
+ .end_primary = END_PRIMARY_CLK,
.gates_data = stm32f469_gates,
.gates_map = stm32f46xx_gate_map,
.gates_num = ARRAY_SIZE(stm32f469_gates),
+ .pll_data = stm32f469_pll,
+ .aux_clk = stm32f469_aux_clk,
+ .aux_clk_num = ARRAY_SIZE(stm32f469_aux_clk),
+};
+
+static const struct stm32f4_clk_data stm32f746_clk_data = {
+ .end_primary = END_PRIMARY_CLK_F7,
+ .gates_data = stm32f746_gates,
+ .gates_map = stm32f746_gate_map,
+ .gates_num = ARRAY_SIZE(stm32f746_gates),
+ .pll_data = stm32f469_pll,
+ .aux_clk = stm32f746_aux_clk,
+ .aux_clk_num = ARRAY_SIZE(stm32f746_aux_clk),
};
static const struct of_device_id stm32f4_of_match[] = {
@@ -638,15 +1321,84 @@ static const struct of_device_id stm32f4_of_match[] = {
.compatible = "st,stm32f469-rcc",
.data = &stm32f469_clk_data
},
+ {
+ .compatible = "st,stm32f746-rcc",
+ .data = &stm32f746_clk_data
+ },
{}
};
+static struct clk_hw *stm32_register_aux_clk(const char *name,
+ const char * const *parent_names, int num_parents,
+ int offset_mux, u8 shift, u8 mask,
+ int offset_gate, u8 bit_idx,
+ unsigned long flags, spinlock_t *lock)
+{
+ struct clk_hw *hw;
+ struct clk_gate *gate = NULL;
+ struct clk_mux *mux = NULL;
+ struct clk_hw *mux_hw = NULL, *gate_hw = NULL;
+ const struct clk_ops *mux_ops = NULL, *gate_ops = NULL;
+
+ if (offset_gate != NO_GATE) {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate) {
+ hw = ERR_PTR(-EINVAL);
+ goto fail;
+ }
+
+ gate->reg = base + offset_gate;
+ gate->bit_idx = bit_idx;
+ gate->flags = 0;
+ gate->lock = lock;
+ gate_hw = &gate->hw;
+ gate_ops = &clk_gate_ops;
+ }
+
+ if (offset_mux != NO_MUX) {
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux) {
+ hw = ERR_PTR(-EINVAL);
+ goto fail;
+ }
+
+ mux->reg = base + offset_mux;
+ mux->shift = shift;
+ mux->mask = mask;
+ mux->flags = 0;
+ mux_hw = &mux->hw;
+ mux_ops = &clk_mux_ops;
+ }
+
+ if (mux_hw == NULL && gate_hw == NULL) {
+ hw = ERR_PTR(-EINVAL);
+ goto fail;
+ }
+
+ hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
+ mux_hw, mux_ops,
+ NULL, NULL,
+ gate_hw, gate_ops,
+ flags);
+
+fail:
+ if (IS_ERR(hw)) {
+ kfree(gate);
+ kfree(mux);
+ }
+
+ return hw;
+}
+
static void __init stm32f4_rcc_init(struct device_node *np)
{
- const char *hse_clk;
+ const char *hse_clk, *i2s_in_clk;
int n;
const struct of_device_id *match;
const struct stm32f4_clk_data *data;
+ unsigned long pllcfgr;
+ const char *pllsrc;
+ unsigned long pllm;
base = of_iomap(np, 0);
if (!base) {
@@ -666,7 +1418,9 @@ static void __init stm32f4_rcc_init(struct device_node *np)
data = match->data;
- clks = kmalloc_array(data->gates_num + END_PRIMARY_CLK,
+ stm32fx_end_primary_clk = data->end_primary;
+
+ clks = kmalloc_array(data->gates_num + stm32fx_end_primary_clk,
sizeof(*clks), GFP_KERNEL);
if (!clks)
goto fail;
@@ -675,12 +1429,54 @@ static void __init stm32f4_rcc_init(struct device_node *np)
hse_clk = of_clk_get_parent_name(np, 0);
- clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
- 16000000, 160000);
- stm32f4_rcc_register_pll(hse_clk, "hsi");
+ i2s_in_clk = of_clk_get_parent_name(np, 1);
+
+ i2s_parents[1] = i2s_in_clk;
+ sai_parents[2] = i2s_in_clk;
+
+ clks[CLK_HSI] = clk_hw_register_fixed_rate_with_accuracy(NULL, "hsi",
+ NULL, 0, 16000000, 160000);
+
+ pllcfgr = readl(base + STM32F4_RCC_PLLCFGR);
+ pllsrc = pllcfgr & BIT(22) ? hse_clk : "hsi";
+ pllm = pllcfgr & 0x3f;
+
+ clk_hw_register_fixed_factor(NULL, "vco_in", pllsrc,
+ 0, 1, pllm);
+
+ stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
+ &stm32f4_clk_lock);
+
+ clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in",
+ &data->pll_data[1], &stm32f4_clk_lock);
+
+ clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in",
+ &data->pll_data[2], &stm32f4_clk_lock);
+
+ for (n = 0; n < MAX_POST_DIV; n++) {
+ const struct stm32f4_pll_post_div_data *post_div;
+ struct clk_hw *hw;
+
+ post_div = &post_div_data[n];
+
+ hw = clk_register_pll_div(post_div->name,
+ post_div->parent,
+ post_div->flag,
+ base + post_div->offset,
+ post_div->shift,
+ post_div->width,
+ post_div->flag_div,
+ post_div->div_table,
+ clks[post_div->pll_num],
+ &stm32f4_clk_lock);
+
+ if (post_div->idx != NO_IDX)
+ clks[post_div->idx] = hw;
+ }
sys_parents[1] = hse_clk;
- clk_register_mux_table(
+
+ clks[CLK_SYSCLK] = clk_hw_register_mux_table(
NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0,
base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock);
@@ -762,11 +1558,39 @@ static void __init stm32f4_rcc_init(struct device_node *np)
goto fail;
}
+ for (n = 0; n < data->aux_clk_num; n++) {
+ const struct stm32_aux_clk *aux_clk;
+ struct clk_hw *hw;
+
+ aux_clk = &data->aux_clk[n];
+
+ hw = stm32_register_aux_clk(aux_clk->name,
+ aux_clk->parent_names, aux_clk->num_parents,
+ aux_clk->offset_mux, aux_clk->shift,
+ aux_clk->mask, aux_clk->offset_gate,
+ aux_clk->bit_idx, aux_clk->flags,
+ &stm32f4_clk_lock);
+
+ if (IS_ERR(hw)) {
+ pr_warn("Unable to register %s clk\n", aux_clk->name);
+ continue;
+ }
+
+ if (aux_clk->idx != NO_IDX)
+ clks[aux_clk->idx] = hw;
+ }
+
+ if (of_device_is_compatible(np, "st,stm32f746-rcc"))
+
+ clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0,
+ 1, 488);
+
of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
return;
fail:
kfree(clks);
iounmap(base);
}
-CLK_OF_DECLARE(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
-CLK_OF_DECLARE(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE_DRIVER(stm32f746_rcc, "st,stm32f746-rcc", stm32f4_rcc_init);
diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c
new file mode 100644
index 000000000000..56741f3cf0a3
--- /dev/null
+++ b/drivers/clk/clk-versaclock5.c
@@ -0,0 +1,791 @@
+/*
+ * Driver for IDT Versaclock 5
+ *
+ * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Possible optimizations:
+ * - Use spread spectrum
+ * - Use integer divider in FOD if applicable
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/rational.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* VersaClock5 registers */
+#define VC5_OTP_CONTROL 0x00
+
+/* Factory-reserved register block */
+#define VC5_RSVD_DEVICE_ID 0x01
+#define VC5_RSVD_ADC_GAIN_7_0 0x02
+#define VC5_RSVD_ADC_GAIN_15_8 0x03
+#define VC5_RSVD_ADC_OFFSET_7_0 0x04
+#define VC5_RSVD_ADC_OFFSET_15_8 0x05
+#define VC5_RSVD_TEMPY 0x06
+#define VC5_RSVD_OFFSET_TBIN 0x07
+#define VC5_RSVD_GAIN 0x08
+#define VC5_RSVD_TEST_NP 0x09
+#define VC5_RSVD_UNUSED 0x0a
+#define VC5_RSVD_BANDGAP_TRIM_UP 0x0b
+#define VC5_RSVD_BANDGAP_TRIM_DN 0x0c
+#define VC5_RSVD_CLK_R_12_CLK_AMP_4 0x0d
+#define VC5_RSVD_CLK_R_34_CLK_AMP_4 0x0e
+#define VC5_RSVD_CLK_AMP_123 0x0f
+
+/* Configuration register block */
+#define VC5_PRIM_SRC_SHDN 0x10
+#define VC5_PRIM_SRC_SHDN_EN_XTAL BIT(7)
+#define VC5_PRIM_SRC_SHDN_EN_CLKIN BIT(6)
+#define VC5_PRIM_SRC_SHDN_SP BIT(1)
+#define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN BIT(0)
+
+#define VC5_VCO_BAND 0x11
+#define VC5_XTAL_X1_LOAD_CAP 0x12
+#define VC5_XTAL_X2_LOAD_CAP 0x13
+#define VC5_REF_DIVIDER 0x15
+#define VC5_REF_DIVIDER_SEL_PREDIV2 BIT(7)
+#define VC5_REF_DIVIDER_REF_DIV(n) ((n) & 0x3f)
+
+#define VC5_VCO_CTRL_AND_PREDIV 0x16
+#define VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV BIT(7)
+
+#define VC5_FEEDBACK_INT_DIV 0x17
+#define VC5_FEEDBACK_INT_DIV_BITS 0x18
+#define VC5_FEEDBACK_FRAC_DIV(n) (0x19 + (n))
+#define VC5_RC_CONTROL0 0x1e
+#define VC5_RC_CONTROL1 0x1f
+/* Register 0x20 is factory reserved */
+
+/* Output divider control for divider 1,2,3,4 */
+#define VC5_OUT_DIV_CONTROL(idx) (0x21 + ((idx) * 0x10))
+#define VC5_OUT_DIV_CONTROL_RESET BIT(7)
+#define VC5_OUT_DIV_CONTROL_SELB_NORM BIT(3)
+#define VC5_OUT_DIV_CONTROL_SEL_EXT BIT(2)
+#define VC5_OUT_DIV_CONTROL_INT_MODE BIT(1)
+#define VC5_OUT_DIV_CONTROL_EN_FOD BIT(0)
+
+#define VC5_OUT_DIV_FRAC(idx, n) (0x22 + ((idx) * 0x10) + (n))
+#define VC5_OUT_DIV_FRAC4_OD_SCEE BIT(1)
+
+#define VC5_OUT_DIV_STEP_SPREAD(idx, n) (0x26 + ((idx) * 0x10) + (n))
+#define VC5_OUT_DIV_SPREAD_MOD(idx, n) (0x29 + ((idx) * 0x10) + (n))
+#define VC5_OUT_DIV_SKEW_INT(idx, n) (0x2b + ((idx) * 0x10) + (n))
+#define VC5_OUT_DIV_INT(idx, n) (0x2d + ((idx) * 0x10) + (n))
+#define VC5_OUT_DIV_SKEW_FRAC(idx) (0x2f + ((idx) * 0x10))
+/* Registers 0x30, 0x40, 0x50 are factory reserved */
+
+/* Clock control register for clock 1,2 */
+#define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n))
+#define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0)
+
+#define VC5_CLK_OE_SHDN 0x68
+#define VC5_CLK_OS_SHDN 0x69
+
+#define VC5_GLOBAL_REGISTER 0x76
+#define VC5_GLOBAL_REGISTER_GLOBAL_RESET BIT(5)
+
+/* PLL/VCO runs between 2.5 GHz and 3.0 GHz */
+#define VC5_PLL_VCO_MIN 2500000000UL
+#define VC5_PLL_VCO_MAX 3000000000UL
+
+/* VC5 Input mux settings */
+#define VC5_MUX_IN_XIN BIT(0)
+#define VC5_MUX_IN_CLKIN BIT(1)
+
+/* Supported IDT VC5 models. */
+enum vc5_model {
+ IDT_VC5_5P49V5923,
+ IDT_VC5_5P49V5933,
+};
+
+struct vc5_driver_data;
+
+struct vc5_hw_data {
+ struct clk_hw hw;
+ struct vc5_driver_data *vc5;
+ u32 div_int;
+ u32 div_frc;
+ unsigned int num;
+};
+
+struct vc5_driver_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ enum vc5_model model;
+
+ struct clk *pin_xin;
+ struct clk *pin_clkin;
+ unsigned char clk_mux_ins;
+ struct clk_hw clk_mux;
+ struct vc5_hw_data clk_pll;
+ struct vc5_hw_data clk_fod[2];
+ struct vc5_hw_data clk_out[3];
+};
+
+static const char * const vc5_mux_names[] = {
+ "mux"
+};
+
+static const char * const vc5_pll_names[] = {
+ "pll"
+};
+
+static const char * const vc5_fod_names[] = {
+ "fod0", "fod1", "fod2", "fod3",
+};
+
+static const char * const vc5_clk_out_names[] = {
+ "out0_sel_i2cb", "out1", "out2", "out3", "out4",
+};
+
+/*
+ * VersaClock5 i2c regmap
+ */
+static bool vc5_regmap_is_writeable(struct device *dev, unsigned int reg)
+{
+ /* Factory reserved regs, make them read-only */
+ if (reg <= 0xf)
+ return false;
+
+ /* Factory reserved regs, make them read-only */
+ if (reg == 0x14 || reg == 0x1c || reg == 0x1d)
+ return false;
+
+ return true;
+}
+
+static const struct regmap_config vc5_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 0x76,
+ .writeable_reg = vc5_regmap_is_writeable,
+};
+
+/*
+ * VersaClock5 input multiplexer between XTAL and CLKIN divider
+ */
+static unsigned char vc5_mux_get_parent(struct clk_hw *hw)
+{
+ struct vc5_driver_data *vc5 =
+ container_of(hw, struct vc5_driver_data, clk_mux);
+ const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN;
+ unsigned int src;
+
+ regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src);
+ src &= mask;
+
+ if (src == VC5_PRIM_SRC_SHDN_EN_XTAL)
+ return 0;
+
+ if (src == VC5_PRIM_SRC_SHDN_EN_CLKIN)
+ return 1;
+
+ dev_warn(&vc5->client->dev,
+ "Invalid clock input configuration (%02x)\n", src);
+ return 0;
+}
+
+static int vc5_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct vc5_driver_data *vc5 =
+ container_of(hw, struct vc5_driver_data, clk_mux);
+ const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN;
+ u8 src;
+
+ if ((index > 1) || !vc5->clk_mux_ins)
+ return -EINVAL;
+
+ if (vc5->clk_mux_ins == (VC5_MUX_IN_CLKIN | VC5_MUX_IN_XIN)) {
+ if (index == 0)
+ src = VC5_PRIM_SRC_SHDN_EN_XTAL;
+ if (index == 1)
+ src = VC5_PRIM_SRC_SHDN_EN_CLKIN;
+ } else {
+ if (index != 0)
+ return -EINVAL;
+
+ if (vc5->clk_mux_ins == VC5_MUX_IN_XIN)
+ src = VC5_PRIM_SRC_SHDN_EN_XTAL;
+ if (vc5->clk_mux_ins == VC5_MUX_IN_CLKIN)
+ src = VC5_PRIM_SRC_SHDN_EN_CLKIN;
+ }
+
+ return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src);
+}
+
+static unsigned long vc5_mux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vc5_driver_data *vc5 =
+ container_of(hw, struct vc5_driver_data, clk_mux);
+ unsigned int prediv, div;
+
+ regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv);
+
+ /* The bypass_prediv is set, PLL fed from Ref_in directly. */
+ if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV)
+ return parent_rate;
+
+ regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div);
+
+ /* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */
+ if (div & VC5_REF_DIVIDER_SEL_PREDIV2)
+ return parent_rate / 2;
+ else
+ return parent_rate / VC5_REF_DIVIDER_REF_DIV(div);
+}
+
+static long vc5_mux_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long idiv;
+
+ /* PLL cannot operate with input clock above 50 MHz. */
+ if (rate > 50000000)
+ return -EINVAL;
+
+ /* CLKIN within range of PLL input, feed directly to PLL. */
+ if (*parent_rate <= 50000000)
+ return *parent_rate;
+
+ idiv = DIV_ROUND_UP(*parent_rate, rate);
+ if (idiv > 127)
+ return -EINVAL;
+
+ return *parent_rate / idiv;
+}
+
+static int vc5_mux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vc5_driver_data *vc5 =
+ container_of(hw, struct vc5_driver_data, clk_mux);
+ unsigned long idiv;
+ u8 div;
+
+ /* CLKIN within range of PLL input, feed directly to PLL. */
+ if (parent_rate <= 50000000) {
+ regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
+ VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV,
+ VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
+ regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00);
+ return 0;
+ }
+
+ idiv = DIV_ROUND_UP(parent_rate, rate);
+
+ /* We have dedicated div-2 predivider. */
+ if (idiv == 2)
+ div = VC5_REF_DIVIDER_SEL_PREDIV2;
+ else
+ div = VC5_REF_DIVIDER_REF_DIV(idiv);
+
+ regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div);
+ regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
+ VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, 0);
+
+ return 0;
+}
+
+static const struct clk_ops vc5_mux_ops = {
+ .set_parent = vc5_mux_set_parent,
+ .get_parent = vc5_mux_get_parent,
+ .recalc_rate = vc5_mux_recalc_rate,
+ .round_rate = vc5_mux_round_rate,
+ .set_rate = vc5_mux_set_rate,
+};
+
+/*
+ * VersaClock5 PLL/VCO
+ */
+static unsigned long vc5_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+ u32 div_int, div_frc;
+ u8 fb[5];
+
+ regmap_bulk_read(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5);
+
+ div_int = (fb[0] << 4) | (fb[1] >> 4);
+ div_frc = (fb[2] << 16) | (fb[3] << 8) | fb[4];
+
+ /* The PLL divider has 12 integer bits and 24 fractional bits */
+ return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24);
+}
+
+static long vc5_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ u32 div_int;
+ u64 div_frc;
+
+ if (rate < VC5_PLL_VCO_MIN)
+ rate = VC5_PLL_VCO_MIN;
+ if (rate > VC5_PLL_VCO_MAX)
+ rate = VC5_PLL_VCO_MAX;
+
+ /* Determine integer part, which is 12 bit wide */
+ div_int = rate / *parent_rate;
+ if (div_int > 0xfff)
+ rate = *parent_rate * 0xfff;
+
+ /* Determine best fractional part, which is 24 bit wide */
+ div_frc = rate % *parent_rate;
+ div_frc *= BIT(24) - 1;
+ do_div(div_frc, *parent_rate);
+
+ hwdata->div_int = div_int;
+ hwdata->div_frc = (u32)div_frc;
+
+ return (*parent_rate * div_int) + ((*parent_rate * div_frc) >> 24);
+}
+
+static int vc5_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+ u8 fb[5];
+
+ fb[0] = hwdata->div_int >> 4;
+ fb[1] = hwdata->div_int << 4;
+ fb[2] = hwdata->div_frc >> 16;
+ fb[3] = hwdata->div_frc >> 8;
+ fb[4] = hwdata->div_frc;
+
+ return regmap_bulk_write(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5);
+}
+
+static const struct clk_ops vc5_pll_ops = {
+ .recalc_rate = vc5_pll_recalc_rate,
+ .round_rate = vc5_pll_round_rate,
+ .set_rate = vc5_pll_set_rate,
+};
+
+static unsigned long vc5_fod_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+ /* VCO frequency is divided by two before entering FOD */
+ u32 f_in = parent_rate / 2;
+ u32 div_int, div_frc;
+ u8 od_int[2];
+ u8 od_frc[4];
+
+ regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_INT(hwdata->num, 0),
+ od_int, 2);
+ regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
+ od_frc, 4);
+
+ div_int = (od_int[0] << 4) | (od_int[1] >> 4);
+ div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) |
+ (od_frc[2] << 6) | (od_frc[3] >> 2);
+
+ /* The PLL divider has 12 integer bits and 30 fractional bits */
+ return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
+}
+
+static long vc5_fod_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ /* VCO frequency is divided by two before entering FOD */
+ u32 f_in = *parent_rate / 2;
+ u32 div_int;
+ u64 div_frc;
+
+ /* Determine integer part, which is 12 bit wide */
+ div_int = f_in / rate;
+ /*
+ * WARNING: The clock chip does not output signal if the integer part
+ * of the divider is 0xfff and fractional part is non-zero.
+ * Clamp the divider at 0xffe to keep the code simple.
+ */
+ if (div_int > 0xffe) {
+ div_int = 0xffe;
+ rate = f_in / div_int;
+ }
+
+ /* Determine best fractional part, which is 30 bit wide */
+ div_frc = f_in % rate;
+ div_frc <<= 24;
+ do_div(div_frc, rate);
+
+ hwdata->div_int = div_int;
+ hwdata->div_frc = (u32)div_frc;
+
+ return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
+}
+
+static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+ u8 data[14] = {
+ hwdata->div_frc >> 22, hwdata->div_frc >> 14,
+ hwdata->div_frc >> 6, hwdata->div_frc << 2,
+ 0, 0, 0, 0, 0,
+ 0, 0,
+ hwdata->div_int >> 4, hwdata->div_int << 4,
+ 0
+ };
+
+ regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
+ data, 14);
+
+ /*
+ * Toggle magic bit in undocumented register for unknown reason.
+ * This is what the IDT timing commander tool does and the chip
+ * datasheet somewhat implies this is needed, but the register
+ * and the bit is not documented.
+ */
+ regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
+ VC5_GLOBAL_REGISTER_GLOBAL_RESET, 0);
+ regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
+ VC5_GLOBAL_REGISTER_GLOBAL_RESET,
+ VC5_GLOBAL_REGISTER_GLOBAL_RESET);
+ return 0;
+}
+
+static const struct clk_ops vc5_fod_ops = {
+ .recalc_rate = vc5_fod_recalc_rate,
+ .round_rate = vc5_fod_round_rate,
+ .set_rate = vc5_fod_set_rate,
+};
+
+static int vc5_clk_out_prepare(struct clk_hw *hw)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+
+ /* Enable the clock buffer */
+ regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
+ VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
+ VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
+ return 0;
+}
+
+static void vc5_clk_out_unprepare(struct clk_hw *hw)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+
+ /* Enable the clock buffer */
+ regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
+ VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0);
+}
+
+static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+ const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM |
+ VC5_OUT_DIV_CONTROL_SEL_EXT |
+ VC5_OUT_DIV_CONTROL_EN_FOD;
+ const u8 fodclkmask = VC5_OUT_DIV_CONTROL_SELB_NORM |
+ VC5_OUT_DIV_CONTROL_EN_FOD;
+ const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM |
+ VC5_OUT_DIV_CONTROL_SEL_EXT;
+ unsigned int src;
+
+ regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
+ src &= mask;
+
+ if ((src & fodclkmask) == VC5_OUT_DIV_CONTROL_EN_FOD)
+ return 0;
+
+ if (src == extclk)
+ return 1;
+
+ dev_warn(&vc5->client->dev,
+ "Invalid clock output configuration (%02x)\n", src);
+ return 0;
+}
+
+static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
+ const u8 mask = VC5_OUT_DIV_CONTROL_RESET |
+ VC5_OUT_DIV_CONTROL_SELB_NORM |
+ VC5_OUT_DIV_CONTROL_SEL_EXT |
+ VC5_OUT_DIV_CONTROL_EN_FOD;
+ const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM |
+ VC5_OUT_DIV_CONTROL_SEL_EXT;
+ u8 src = VC5_OUT_DIV_CONTROL_RESET;
+
+ if (index == 0)
+ src |= VC5_OUT_DIV_CONTROL_EN_FOD;
+ else
+ src |= extclk;
+
+ return regmap_update_bits(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num),
+ mask, src);
+}
+
+static const struct clk_ops vc5_clk_out_ops = {
+ .prepare = vc5_clk_out_prepare,
+ .unprepare = vc5_clk_out_unprepare,
+ .set_parent = vc5_clk_out_set_parent,
+ .get_parent = vc5_clk_out_get_parent,
+};
+
+static struct clk_hw *vc5_of_clk_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct vc5_driver_data *vc5 = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx > 2)
+ return ERR_PTR(-EINVAL);
+
+ return &vc5->clk_out[idx].hw;
+}
+
+static int vc5_map_index_to_output(const enum vc5_model model,
+ const unsigned int n)
+{
+ switch (model) {
+ case IDT_VC5_5P49V5933:
+ return (n == 0) ? 0 : 3;
+ case IDT_VC5_5P49V5923:
+ default:
+ return n;
+ }
+}
+
+static const struct of_device_id clk_vc5_of_match[];
+
+static int vc5_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct of_device_id *of_id =
+ of_match_device(clk_vc5_of_match, &client->dev);
+ struct vc5_driver_data *vc5;
+ struct clk_init_data init;
+ const char *parent_names[2];
+ unsigned int n, idx;
+ int ret;
+
+ vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL);
+ if (vc5 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, vc5);
+ vc5->client = client;
+ vc5->model = (enum vc5_model)of_id->data;
+
+ vc5->pin_xin = devm_clk_get(&client->dev, "xin");
+ if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ vc5->pin_clkin = devm_clk_get(&client->dev, "clkin");
+ if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config);
+ if (IS_ERR(vc5->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(vc5->regmap);
+ }
+
+ /* Register clock input mux */
+ memset(&init, 0, sizeof(init));
+
+ if (!IS_ERR(vc5->pin_xin)) {
+ vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
+ parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
+ } else if (vc5->model == IDT_VC5_5P49V5933) {
+ /* IDT VC5 5P49V5933 has built-in oscilator. */
+ vc5->pin_xin = clk_register_fixed_rate(&client->dev,
+ "internal-xtal", NULL,
+ 0, 25000000);
+ if (IS_ERR(vc5->pin_xin))
+ return PTR_ERR(vc5->pin_xin);
+ vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
+ parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
+ }
+
+ if (!IS_ERR(vc5->pin_clkin)) {
+ vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN;
+ parent_names[init.num_parents++] =
+ __clk_get_name(vc5->pin_clkin);
+ }
+
+ if (!init.num_parents) {
+ dev_err(&client->dev, "no input clock specified!\n");
+ return -EINVAL;
+ }
+
+ init.name = vc5_mux_names[0];
+ init.ops = &vc5_mux_ops;
+ init.flags = 0;
+ init.parent_names = parent_names;
+ vc5->clk_mux.init = &init;
+ ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux);
+ if (ret) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ goto err_clk;
+ }
+
+ /* Register PLL */
+ memset(&init, 0, sizeof(init));
+ init.name = vc5_pll_names[0];
+ init.ops = &vc5_pll_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = vc5_mux_names;
+ init.num_parents = 1;
+ vc5->clk_pll.num = 0;
+ vc5->clk_pll.vc5 = vc5;
+ vc5->clk_pll.hw.init = &init;
+ ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw);
+ if (ret) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ goto err_clk;
+ }
+
+ /* Register FODs */
+ for (n = 0; n < 2; n++) {
+ idx = vc5_map_index_to_output(vc5->model, n);
+ memset(&init, 0, sizeof(init));
+ init.name = vc5_fod_names[idx];
+ init.ops = &vc5_fod_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = vc5_pll_names;
+ init.num_parents = 1;
+ vc5->clk_fod[n].num = idx;
+ vc5->clk_fod[n].vc5 = vc5;
+ vc5->clk_fod[n].hw.init = &init;
+ ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw);
+ if (ret) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ goto err_clk;
+ }
+ }
+
+ /* Register MUX-connected OUT0_I2C_SELB output */
+ memset(&init, 0, sizeof(init));
+ init.name = vc5_clk_out_names[0];
+ init.ops = &vc5_clk_out_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = vc5_mux_names;
+ init.num_parents = 1;
+ vc5->clk_out[0].num = idx;
+ vc5->clk_out[0].vc5 = vc5;
+ vc5->clk_out[0].hw.init = &init;
+ ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw);
+ if (ret) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ goto err_clk;
+ }
+
+ /* Register FOD-connected OUTx outputs */
+ for (n = 1; n < 3; n++) {
+ idx = vc5_map_index_to_output(vc5->model, n - 1);
+ parent_names[0] = vc5_fod_names[idx];
+ if (n == 1)
+ parent_names[1] = vc5_mux_names[0];
+ else
+ parent_names[1] = vc5_clk_out_names[n - 1];
+
+ memset(&init, 0, sizeof(init));
+ init.name = vc5_clk_out_names[idx + 1];
+ init.ops = &vc5_clk_out_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = 2;
+ vc5->clk_out[n].num = idx;
+ vc5->clk_out[n].vc5 = vc5;
+ vc5->clk_out[n].hw.init = &init;
+ ret = devm_clk_hw_register(&client->dev,
+ &vc5->clk_out[n].hw);
+ if (ret) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ goto err_clk;
+ }
+ }
+
+ ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);
+ if (ret) {
+ dev_err(&client->dev, "unable to add clk provider\n");
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ if (vc5->model == IDT_VC5_5P49V5933)
+ clk_unregister_fixed_rate(vc5->pin_xin);
+ return ret;
+}
+
+static int vc5_remove(struct i2c_client *client)
+{
+ struct vc5_driver_data *vc5 = i2c_get_clientdata(client);
+
+ of_clk_del_provider(client->dev.of_node);
+
+ if (vc5->model == IDT_VC5_5P49V5933)
+ clk_unregister_fixed_rate(vc5->pin_xin);
+
+ return 0;
+}
+
+static const struct i2c_device_id vc5_id[] = {
+ { "5p49v5923", .driver_data = IDT_VC5_5P49V5923 },
+ { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vc5_id);
+
+static const struct of_device_id clk_vc5_of_match[] = {
+ { .compatible = "idt,5p49v5923", .data = (void *)IDT_VC5_5P49V5923 },
+ { .compatible = "idt,5p49v5933", .data = (void *)IDT_VC5_5P49V5933 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, clk_vc5_of_match);
+
+static struct i2c_driver vc5_driver = {
+ .driver = {
+ .name = "vc5",
+ .of_match_table = clk_vc5_of_match,
+ },
+ .probe = vc5_probe,
+ .remove = vc5_remove,
+ .id_table = vc5_id,
+};
+module_i2c_driver(vc5_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("IDT VersaClock 5 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index 0621fbfb4beb..a47960aacfa5 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -97,7 +97,8 @@ static int wm831x_fll_prepare(struct clk_hw *hw)
if (ret != 0)
dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret);
- usleep_range(2000, 2000);
+ /* wait 2-3 ms for new frequency taking effect */
+ usleep_range(2000, 3000);
return ret;
}
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig
index cbed6602172b..7098bfd32b1b 100644
--- a/drivers/clk/hisilicon/Kconfig
+++ b/drivers/clk/hisilicon/Kconfig
@@ -14,6 +14,13 @@ config COMMON_CLK_HI3519
help
Build the clock driver for hi3519.
+config COMMON_CLK_HI3660
+ bool "Hi3660 Clock Driver"
+ depends on ARCH_HISI || COMPILE_TEST
+ default ARCH_HISI
+ help
+ Build the clock driver for hi3660.
+
config COMMON_CLK_HI3798CV200
tristate "Hi3798CV200 Clock Driver"
depends on ARCH_HISI || COMPILE_TEST
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index 4eec5e511e4c..1e4c3ddbad84 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o
obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o
obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o
+obj-$(CONFIG_COMMON_CLK_HI3660) += clk-hi3660.o
obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o
obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o
obj-$(CONFIG_RESET_HISI) += reset.o
diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c
new file mode 100644
index 000000000000..96a9697b06cf
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3660.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2016-2017 Linaro Ltd.
+ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <dt-bindings/clock/hi3660-clock.h>
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk.h"
+
+static const struct hisi_fixed_rate_clock hi3660_fixed_rate_clks[] = {
+ { HI3660_CLKIN_SYS, "clkin_sys", NULL, 0, 19200000, },
+ { HI3660_CLKIN_REF, "clkin_ref", NULL, 0, 32764, },
+ { HI3660_CLK_FLL_SRC, "clk_fll_src", NULL, 0, 128000000, },
+ { HI3660_CLK_PPLL0, "clk_ppll0", NULL, 0, 1600000000, },
+ { HI3660_CLK_PPLL1, "clk_ppll1", NULL, 0, 1866000000, },
+ { HI3660_CLK_PPLL2, "clk_ppll2", NULL, 0, 960000000, },
+ { HI3660_CLK_PPLL3, "clk_ppll3", NULL, 0, 1290000000, },
+ { HI3660_CLK_SCPLL, "clk_scpll", NULL, 0, 245760000, },
+ { HI3660_PCLK, "pclk", NULL, 0, 20000000, },
+ { HI3660_CLK_UART0_DBG, "clk_uart0_dbg", NULL, 0, 19200000, },
+ { HI3660_CLK_UART6, "clk_uart6", NULL, 0, 19200000, },
+ { HI3660_OSC32K, "osc32k", NULL, 0, 32764, },
+ { HI3660_OSC19M, "osc19m", NULL, 0, 19200000, },
+ { HI3660_CLK_480M, "clk_480m", NULL, 0, 480000000, },
+ { HI3660_CLK_INV, "clk_inv", NULL, 0, 10000000, },
+};
+
+/* crgctrl */
+static const struct hisi_fixed_factor_clock hi3660_crg_fixed_factor_clks[] = {
+ { HI3660_FACTOR_UART3, "clk_factor_uart3", "iomcu_peri0", 1, 8, 0, },
+ { HI3660_CLK_FACTOR_MMC, "clk_factor_mmc", "clkin_sys", 1, 6, 0, },
+ { HI3660_CLK_GATE_I2C0, "clk_gate_i2c0", "clk_i2c0_iomcu", 1, 4, 0, },
+ { HI3660_CLK_GATE_I2C1, "clk_gate_i2c1", "clk_i2c1_iomcu", 1, 4, 0, },
+ { HI3660_CLK_GATE_I2C2, "clk_gate_i2c2", "clk_i2c2_iomcu", 1, 4, 0, },
+ { HI3660_CLK_GATE_I2C6, "clk_gate_i2c6", "clk_i2c6_iomcu", 1, 4, 0, },
+ { HI3660_CLK_DIV_SYSBUS, "clk_div_sysbus", "clk_mux_sysbus", 1, 7, 0, },
+ { HI3660_CLK_DIV_320M, "clk_div_320m", "clk_320m_pll_gt", 1, 5, 0, },
+ { HI3660_CLK_DIV_A53, "clk_div_a53hpm", "clk_a53hpm_andgt", 1, 2, 0, },
+ { HI3660_CLK_GATE_SPI0, "clk_gate_spi0", "clk_ppll0", 1, 8, 0, },
+ { HI3660_CLK_GATE_SPI2, "clk_gate_spi2", "clk_ppll0", 1, 8, 0, },
+ { HI3660_PCIEPHY_REF, "clk_pciephy_ref", "clk_div_pciephy", 1, 1, 0, },
+ { HI3660_CLK_ABB_USB, "clk_abb_usb", "clk_gate_usb_tcxo_en", 1, 1, 0 },
+};
+
+static const struct hisi_gate_clock hi3660_crgctrl_gate_sep_clks[] = {
+ { HI3660_HCLK_GATE_SDIO0, "hclk_gate_sdio0", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x0, 21, 0, },
+ { HI3660_HCLK_GATE_SD, "hclk_gate_sd", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x0, 30, 0, },
+ { HI3660_CLK_GATE_AOMM, "clk_gate_aomm", "clk_div_aomm",
+ CLK_SET_RATE_PARENT, 0x0, 31, 0, },
+ { HI3660_PCLK_GPIO0, "pclk_gpio0", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 0, 0, },
+ { HI3660_PCLK_GPIO1, "pclk_gpio1", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 1, 0, },
+ { HI3660_PCLK_GPIO2, "pclk_gpio2", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 2, 0, },
+ { HI3660_PCLK_GPIO3, "pclk_gpio3", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 3, 0, },
+ { HI3660_PCLK_GPIO4, "pclk_gpio4", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 4, 0, },
+ { HI3660_PCLK_GPIO5, "pclk_gpio5", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 5, 0, },
+ { HI3660_PCLK_GPIO6, "pclk_gpio6", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 6, 0, },
+ { HI3660_PCLK_GPIO7, "pclk_gpio7", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 7, 0, },
+ { HI3660_PCLK_GPIO8, "pclk_gpio8", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 8, 0, },
+ { HI3660_PCLK_GPIO9, "pclk_gpio9", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 9, 0, },
+ { HI3660_PCLK_GPIO10, "pclk_gpio10", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 10, 0, },
+ { HI3660_PCLK_GPIO11, "pclk_gpio11", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 11, 0, },
+ { HI3660_PCLK_GPIO12, "pclk_gpio12", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 12, 0, },
+ { HI3660_PCLK_GPIO13, "pclk_gpio13", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 13, 0, },
+ { HI3660_PCLK_GPIO14, "pclk_gpio14", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 14, 0, },
+ { HI3660_PCLK_GPIO15, "pclk_gpio15", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 15, 0, },
+ { HI3660_PCLK_GPIO16, "pclk_gpio16", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 16, 0, },
+ { HI3660_PCLK_GPIO17, "pclk_gpio17", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 17, 0, },
+ { HI3660_PCLK_GPIO18, "pclk_gpio18", "clk_div_ioperi",
+ CLK_SET_RATE_PARENT, 0x10, 18, 0, },
+ { HI3660_PCLK_GPIO19, "pclk_gpio19", "clk_div_ioperi",
+ CLK_SET_RATE_PARENT, 0x10, 19, 0, },
+ { HI3660_PCLK_GPIO20, "pclk_gpio20", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 20, 0, },
+ { HI3660_PCLK_GPIO21, "pclk_gpio21", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x10, 21, 0, },
+ { HI3660_CLK_GATE_SPI3, "clk_gate_spi3", "clk_div_ioperi",
+ CLK_SET_RATE_PARENT, 0x10, 30, 0, },
+ { HI3660_CLK_GATE_I2C7, "clk_gate_i2c7", "clk_mux_i2c",
+ CLK_SET_RATE_PARENT, 0x10, 31, 0, },
+ { HI3660_CLK_GATE_I2C3, "clk_gate_i2c3", "clk_mux_i2c",
+ CLK_SET_RATE_PARENT, 0x20, 7, 0, },
+ { HI3660_CLK_GATE_SPI1, "clk_gate_spi1", "clk_mux_spi",
+ CLK_SET_RATE_PARENT, 0x20, 9, 0, },
+ { HI3660_CLK_GATE_UART1, "clk_gate_uart1", "clk_mux_uarth",
+ CLK_SET_RATE_PARENT, 0x20, 11, 0, },
+ { HI3660_CLK_GATE_UART2, "clk_gate_uart2", "clk_mux_uart1",
+ CLK_SET_RATE_PARENT, 0x20, 12, 0, },
+ { HI3660_CLK_GATE_UART4, "clk_gate_uart4", "clk_mux_uarth",
+ CLK_SET_RATE_PARENT, 0x20, 14, 0, },
+ { HI3660_CLK_GATE_UART5, "clk_gate_uart5", "clk_mux_uart1",
+ CLK_SET_RATE_PARENT, 0x20, 15, 0, },
+ { HI3660_CLK_GATE_I2C4, "clk_gate_i2c4", "clk_mux_i2c",
+ CLK_SET_RATE_PARENT, 0x20, 27, 0, },
+ { HI3660_CLK_GATE_DMAC, "clk_gate_dmac", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x30, 1, 0, },
+ { HI3660_PCLK_GATE_DSS, "pclk_gate_dss", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x30, 12, 0, },
+ { HI3660_ACLK_GATE_DSS, "aclk_gate_dss", "clk_gate_vivobus",
+ CLK_SET_RATE_PARENT, 0x30, 13, 0, },
+ { HI3660_CLK_GATE_LDI1, "clk_gate_ldi1", "clk_div_ldi1",
+ CLK_SET_RATE_PARENT, 0x30, 14, 0, },
+ { HI3660_CLK_GATE_LDI0, "clk_gate_ldi0", "clk_div_ldi0",
+ CLK_SET_RATE_PARENT, 0x30, 15, 0, },
+ { HI3660_CLK_GATE_VIVOBUS, "clk_gate_vivobus", "clk_div_vivobus",
+ CLK_SET_RATE_PARENT, 0x30, 16, 0, },
+ { HI3660_CLK_GATE_EDC0, "clk_gate_edc0", "clk_div_edc0",
+ CLK_SET_RATE_PARENT, 0x30, 17, 0, },
+ { HI3660_CLK_GATE_TXDPHY0_CFG, "clk_gate_txdphy0_cfg", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 28, 0, },
+ { HI3660_CLK_GATE_TXDPHY0_REF, "clk_gate_txdphy0_ref", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 29, 0, },
+ { HI3660_CLK_GATE_TXDPHY1_CFG, "clk_gate_txdphy1_cfg", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 30, 0, },
+ { HI3660_CLK_GATE_TXDPHY1_REF, "clk_gate_txdphy1_ref", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x30, 31, 0, },
+ { HI3660_ACLK_GATE_USB3OTG, "aclk_gate_usb3otg", "clk_div_mmc0bus",
+ CLK_SET_RATE_PARENT, 0x40, 1, 0, },
+ { HI3660_CLK_GATE_SPI4, "clk_gate_spi4", "clk_mux_spi",
+ CLK_SET_RATE_PARENT, 0x40, 4, 0, },
+ { HI3660_CLK_GATE_SD, "clk_gate_sd", "clk_mux_sd_sys",
+ CLK_SET_RATE_PARENT, 0x40, 17, 0, },
+ { HI3660_CLK_GATE_SDIO0, "clk_gate_sdio0", "clk_mux_sdio_sys",
+ CLK_SET_RATE_PARENT, 0x40, 19, 0, },
+ { HI3660_CLK_GATE_UFS_SUBSYS, "clk_gate_ufs_subsys", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x50, 21, 0, },
+ { HI3660_PCLK_GATE_DSI0, "pclk_gate_dsi0", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x50, 28, 0, },
+ { HI3660_PCLK_GATE_DSI1, "pclk_gate_dsi1", "clk_div_cfgbus",
+ CLK_SET_RATE_PARENT, 0x50, 29, 0, },
+ { HI3660_ACLK_GATE_PCIE, "aclk_gate_pcie", "clk_div_mmc1bus",
+ CLK_SET_RATE_PARENT, 0x420, 5, 0, },
+ { HI3660_PCLK_GATE_PCIE_SYS, "pclk_gate_pcie_sys", "clk_div_mmc1bus",
+ CLK_SET_RATE_PARENT, 0x420, 7, 0, },
+ { HI3660_CLK_GATE_PCIEAUX, "clk_gate_pcieaux", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x420, 8, 0, },
+ { HI3660_PCLK_GATE_PCIE_PHY, "pclk_gate_pcie_phy", "clk_div_mmc1bus",
+ CLK_SET_RATE_PARENT, 0x420, 9, 0, },
+};
+
+static const struct hisi_gate_clock hi3660_crgctrl_gate_clks[] = {
+ { HI3660_CLK_ANDGT_LDI0, "clk_andgt_ldi0", "clk_mux_ldi0",
+ CLK_SET_RATE_PARENT, 0xf0, 6, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_LDI1, "clk_andgt_ldi1", "clk_mux_ldi1",
+ CLK_SET_RATE_PARENT, 0xf0, 7, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_EDC0, "clk_andgt_edc0", "clk_mux_edc0",
+ CLK_SET_RATE_PARENT, 0xf0, 8, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_GATE_UFSPHY_GT, "clk_gate_ufsphy_gt", "clk_div_ufsperi",
+ CLK_SET_RATE_PARENT, 0xf4, 1, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_MMC, "clk_andgt_mmc", "clk_mux_mmc_pll",
+ CLK_SET_RATE_PARENT, 0xf4, 2, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_SD, "clk_andgt_sd", "clk_mux_sd_pll",
+ CLK_SET_RATE_PARENT, 0xf4, 3, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_A53HPM_ANDGT, "clk_a53hpm_andgt", "clk_mux_a53hpm",
+ CLK_SET_RATE_PARENT, 0xf4, 7, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_SDIO, "clk_andgt_sdio", "clk_mux_sdio_pll",
+ CLK_SET_RATE_PARENT, 0xf4, 8, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_UART0, "clk_andgt_uart0", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 9, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_UART1, "clk_andgt_uart1", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 10, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_UARTH, "clk_andgt_uarth", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 11, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_ANDGT_SPI, "clk_andgt_spi", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xf4, 13, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_VIVOBUS_ANDGT, "clk_vivobus_andgt", "clk_mux_vivobus",
+ CLK_SET_RATE_PARENT, 0xf8, 1, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_AOMM_ANDGT, "clk_aomm_andgt", "clk_ppll2",
+ CLK_SET_RATE_PARENT, 0xf8, 3, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_320M_PLL_GT, "clk_320m_pll_gt", "clk_mux_320m",
+ CLK_SET_RATE_PARENT, 0xf8, 10, 0, },
+ { HI3660_AUTODIV_EMMC0BUS, "autodiv_emmc0bus", "autodiv_sysbus",
+ CLK_SET_RATE_PARENT, 0x404, 1, CLK_GATE_HIWORD_MASK, },
+ { HI3660_AUTODIV_SYSBUS, "autodiv_sysbus", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0x404, 5, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_GATE_UFSPHY_CFG, "clk_gate_ufsphy_cfg",
+ "clk_div_ufsphy_cfg", CLK_SET_RATE_PARENT, 0x420, 12, 0, },
+ { HI3660_CLK_GATE_UFSIO_REF, "clk_gate_ufsio_ref",
+ "clk_gate_ufs_tcxo_en", CLK_SET_RATE_PARENT, 0x420, 14, 0, },
+};
+
+static const char *const
+clk_mux_sdio_sys_p[] = {"clk_factor_mmc", "clk_div_sdio",};
+static const char *const
+clk_mux_sd_sys_p[] = {"clk_factor_mmc", "clk_div_sd",};
+static const char *const
+clk_mux_pll_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll2",};
+static const char *const
+clk_mux_pll0123_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll3",};
+static const char *const
+clk_mux_edc0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll1", "clk_inv",
+ "clk_ppll2", "clk_inv", "clk_inv", "clk_inv",
+ "clk_ppll3", "clk_inv", "clk_inv", "clk_inv",
+ "clk_inv", "clk_inv", "clk_inv", "clk_inv",};
+static const char *const
+clk_mux_ldi0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll2", "clk_inv",
+ "clk_ppll1", "clk_inv", "clk_inv", "clk_inv",
+ "clk_ppll3", "clk_inv", "clk_inv", "clk_inv",
+ "clk_inv", "clk_inv", "clk_inv", "clk_inv",};
+static const char *const
+clk_mux_uart0_p[] = {"clkin_sys", "clk_div_uart0",};
+static const char *const
+clk_mux_uart1_p[] = {"clkin_sys", "clk_div_uart1",};
+static const char *const
+clk_mux_uarth_p[] = {"clkin_sys", "clk_div_uarth",};
+static const char *const
+clk_mux_pll02p[] = {"clk_ppll0", "clk_ppll2",};
+static const char *const
+clk_mux_ioperi_p[] = {"clk_div_320m", "clk_div_a53hpm",};
+static const char *const
+clk_mux_spi_p[] = {"clkin_sys", "clk_div_spi",};
+static const char *const
+clk_mux_i2c_p[] = {"clkin_sys", "clk_div_i2c",};
+
+static const struct hisi_mux_clock hi3660_crgctrl_mux_clks[] = {
+ { HI3660_CLK_MUX_SYSBUS, "clk_mux_sysbus", clk_mux_sdio_sys_p,
+ ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xac, 0, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_UART0, "clk_mux_uart0", clk_mux_uart0_p,
+ ARRAY_SIZE(clk_mux_uart0_p), CLK_SET_RATE_PARENT, 0xac, 2, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_UART1, "clk_mux_uart1", clk_mux_uart1_p,
+ ARRAY_SIZE(clk_mux_uart1_p), CLK_SET_RATE_PARENT, 0xac, 3, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_UARTH, "clk_mux_uarth", clk_mux_uarth_p,
+ ARRAY_SIZE(clk_mux_uarth_p), CLK_SET_RATE_PARENT, 0xac, 4, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SPI, "clk_mux_spi", clk_mux_spi_p,
+ ARRAY_SIZE(clk_mux_spi_p), CLK_SET_RATE_PARENT, 0xac, 8, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_I2C, "clk_mux_i2c", clk_mux_i2c_p,
+ ARRAY_SIZE(clk_mux_i2c_p), CLK_SET_RATE_PARENT, 0xac, 13, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_MMC_PLL, "clk_mux_mmc_pll", clk_mux_pll02p,
+ ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xb4, 0, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_LDI1, "clk_mux_ldi1", clk_mux_ldi0_p,
+ ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 8, 4,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_LDI0, "clk_mux_ldi0", clk_mux_ldi0_p,
+ ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 12, 4,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SD_PLL, "clk_mux_sd_pll", clk_mux_pll_p,
+ ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xb8, 4, 2,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SD_SYS, "clk_mux_sd_sys", clk_mux_sd_sys_p,
+ ARRAY_SIZE(clk_mux_sd_sys_p), CLK_SET_RATE_PARENT, 0xb8, 6, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_EDC0, "clk_mux_edc0", clk_mux_edc0_p,
+ ARRAY_SIZE(clk_mux_edc0_p), CLK_SET_RATE_PARENT, 0xbc, 6, 4,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SDIO_SYS, "clk_mux_sdio_sys", clk_mux_sdio_sys_p,
+ ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xc0, 6, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_SDIO_PLL, "clk_mux_sdio_pll", clk_mux_pll_p,
+ ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xc0, 4, 2,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_VIVOBUS, "clk_mux_vivobus", clk_mux_pll0123_p,
+ ARRAY_SIZE(clk_mux_pll0123_p), CLK_SET_RATE_PARENT, 0xd0, 12, 2,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_A53HPM, "clk_mux_a53hpm", clk_mux_pll02p,
+ ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xd4, 9, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_320M, "clk_mux_320m", clk_mux_pll02p,
+ ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0x100, 0, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_MUX_IOPERI, "clk_mux_ioperi", clk_mux_ioperi_p,
+ ARRAY_SIZE(clk_mux_ioperi_p), CLK_SET_RATE_PARENT, 0x108, 10, 1,
+ CLK_MUX_HIWORD_MASK, },
+};
+
+static const struct hisi_divider_clock hi3660_crgctrl_divider_clks[] = {
+ { HI3660_CLK_DIV_UART0, "clk_div_uart0", "clk_andgt_uart0",
+ CLK_SET_RATE_PARENT, 0xb0, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UART1, "clk_div_uart1", "clk_andgt_uart1",
+ CLK_SET_RATE_PARENT, 0xb0, 8, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UARTH, "clk_div_uarth", "clk_andgt_uarth",
+ CLK_SET_RATE_PARENT, 0xb0, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_MMC, "clk_div_mmc", "clk_andgt_mmc",
+ CLK_SET_RATE_PARENT, 0xb4, 3, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_SD, "clk_div_sd", "clk_andgt_sd",
+ CLK_SET_RATE_PARENT, 0xb8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_EDC0, "clk_div_edc0", "clk_andgt_edc0",
+ CLK_SET_RATE_PARENT, 0xbc, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_LDI0, "clk_div_ldi0", "clk_andgt_ldi0",
+ CLK_SET_RATE_PARENT, 0xbc, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_SDIO, "clk_div_sdio", "clk_andgt_sdio",
+ CLK_SET_RATE_PARENT, 0xc0, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_LDI1, "clk_div_ldi1", "clk_andgt_ldi1",
+ CLK_SET_RATE_PARENT, 0xc0, 8, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_SPI, "clk_div_spi", "clk_andgt_spi",
+ CLK_SET_RATE_PARENT, 0xc4, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_VIVOBUS, "clk_div_vivobus", "clk_vivobus_andgt",
+ CLK_SET_RATE_PARENT, 0xd0, 7, 5, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_I2C, "clk_div_i2c", "clk_div_320m",
+ CLK_SET_RATE_PARENT, 0xe8, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UFSPHY, "clk_div_ufsphy_cfg", "clk_gate_ufsphy_gt",
+ CLK_SET_RATE_PARENT, 0xe8, 9, 2, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_CFGBUS, "clk_div_cfgbus", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0xec, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_MMC0BUS, "clk_div_mmc0bus", "autodiv_emmc0bus",
+ CLK_SET_RATE_PARENT, 0xec, 2, 1, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_MMC1BUS, "clk_div_mmc1bus", "clk_div_sysbus",
+ CLK_SET_RATE_PARENT, 0xec, 3, 1, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_UFSPERI, "clk_div_ufsperi", "clk_gate_ufs_subsys",
+ CLK_SET_RATE_PARENT, 0xec, 14, 1, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_AOMM, "clk_div_aomm", "clk_aomm_andgt",
+ CLK_SET_RATE_PARENT, 0x100, 7, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_IOPERI, "clk_div_ioperi", "clk_mux_ioperi",
+ CLK_SET_RATE_PARENT, 0x108, 11, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+};
+
+/* clk_pmuctrl */
+/* pmu register need shift 2 bits */
+static const struct hisi_gate_clock hi3660_pmu_gate_clks[] = {
+ { HI3660_GATE_ABB_192, "clk_gate_abb_192", "clkin_sys",
+ CLK_SET_RATE_PARENT, (0x10a << 2), 3, 0, },
+};
+
+/* clk_pctrl */
+static const struct hisi_gate_clock hi3660_pctrl_gate_clks[] = {
+ { HI3660_GATE_UFS_TCXO_EN, "clk_gate_ufs_tcxo_en",
+ "clk_gate_abb_192", CLK_SET_RATE_PARENT, 0x10, 0,
+ CLK_GATE_HIWORD_MASK, },
+ { HI3660_GATE_USB_TCXO_EN, "clk_gate_usb_tcxo_en", "clk_gate_abb_192",
+ CLK_SET_RATE_PARENT, 0x10, 1, CLK_GATE_HIWORD_MASK, },
+};
+
+/* clk_sctrl */
+static const struct hisi_gate_clock hi3660_sctrl_gate_sep_clks[] = {
+ { HI3660_PCLK_AO_GPIO0, "pclk_ao_gpio0", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 11, 0, },
+ { HI3660_PCLK_AO_GPIO1, "pclk_ao_gpio1", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 12, 0, },
+ { HI3660_PCLK_AO_GPIO2, "pclk_ao_gpio2", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 13, 0, },
+ { HI3660_PCLK_AO_GPIO3, "pclk_ao_gpio3", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 14, 0, },
+ { HI3660_PCLK_AO_GPIO4, "pclk_ao_gpio4", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 21, 0, },
+ { HI3660_PCLK_AO_GPIO5, "pclk_ao_gpio5", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 22, 0, },
+ { HI3660_PCLK_AO_GPIO6, "pclk_ao_gpio6", "clk_div_aobus",
+ CLK_SET_RATE_PARENT, 0x160, 25, 0, },
+ { HI3660_PCLK_GATE_MMBUF, "pclk_gate_mmbuf", "pclk_div_mmbuf",
+ CLK_SET_RATE_PARENT, 0x170, 23, 0, },
+ { HI3660_CLK_GATE_DSS_AXI_MM, "clk_gate_dss_axi_mm", "aclk_mux_mmbuf",
+ CLK_SET_RATE_PARENT, 0x170, 24, 0, },
+};
+
+static const struct hisi_gate_clock hi3660_sctrl_gate_clks[] = {
+ { HI3660_PCLK_MMBUF_ANDGT, "pclk_mmbuf_andgt", "clk_sw_mmbuf",
+ CLK_SET_RATE_PARENT, 0x258, 7, CLK_GATE_HIWORD_MASK, },
+ { HI3660_CLK_MMBUF_PLL_ANDGT, "clk_mmbuf_pll_andgt", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x260, 11, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_FLL_MMBUF_ANDGT, "clk_fll_mmbuf_andgt", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x260, 12, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_SYS_MMBUF_ANDGT, "clk_sys_mmbuf_andgt", "clkin_sys",
+ CLK_SET_RATE_PARENT, 0x260, 13, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_GATE_PCIEPHY_GT, "clk_gate_pciephy_gt", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x268, 11, CLK_DIVIDER_HIWORD_MASK, 0, },
+};
+
+static const char *const
+aclk_mux_mmbuf_p[] = {"aclk_div_mmbuf", "clk_gate_aomm",};
+static const char *const
+clk_sw_mmbuf_p[] = {"clk_sys_mmbuf_andgt", "clk_fll_mmbuf_andgt",
+ "aclk_mux_mmbuf", "aclk_mux_mmbuf"};
+
+static const struct hisi_mux_clock hi3660_sctrl_mux_clks[] = {
+ { HI3660_ACLK_MUX_MMBUF, "aclk_mux_mmbuf", aclk_mux_mmbuf_p,
+ ARRAY_SIZE(aclk_mux_mmbuf_p), CLK_SET_RATE_PARENT, 0x250, 12, 1,
+ CLK_MUX_HIWORD_MASK, },
+ { HI3660_CLK_SW_MMBUF, "clk_sw_mmbuf", clk_sw_mmbuf_p,
+ ARRAY_SIZE(clk_sw_mmbuf_p), CLK_SET_RATE_PARENT, 0x258, 8, 2,
+ CLK_MUX_HIWORD_MASK, },
+};
+
+static const struct hisi_divider_clock hi3660_sctrl_divider_clks[] = {
+ { HI3660_CLK_DIV_AOBUS, "clk_div_aobus", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_PCLK_DIV_MMBUF, "pclk_div_mmbuf", "pclk_mmbuf_andgt",
+ CLK_SET_RATE_PARENT, 0x258, 10, 2, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_ACLK_DIV_MMBUF, "aclk_div_mmbuf", "clk_mmbuf_pll_andgt",
+ CLK_SET_RATE_PARENT, 0x258, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+ { HI3660_CLK_DIV_PCIEPHY, "clk_div_pciephy", "clk_gate_pciephy_gt",
+ CLK_SET_RATE_PARENT, 0x268, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, },
+};
+
+/* clk_iomcu */
+static const struct hisi_gate_clock hi3660_iomcu_gate_sep_clks[] = {
+ { HI3660_CLK_I2C0_IOMCU, "clk_i2c0_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 3, 0, },
+ { HI3660_CLK_I2C1_IOMCU, "clk_i2c1_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 4, 0, },
+ { HI3660_CLK_I2C2_IOMCU, "clk_i2c2_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 5, 0, },
+ { HI3660_CLK_I2C6_IOMCU, "clk_i2c6_iomcu", "clk_fll_src",
+ CLK_SET_RATE_PARENT, 0x10, 27, 0, },
+ { HI3660_CLK_IOMCU_PERI0, "iomcu_peri0", "clk_ppll0",
+ CLK_SET_RATE_PARENT, 0x90, 0, 0, },
+};
+
+static void hi3660_clk_iomcu_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_iomcu_gate_sep_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_gate_sep(hi3660_iomcu_gate_sep_clks,
+ ARRAY_SIZE(hi3660_iomcu_gate_sep_clks),
+ clk_data);
+}
+
+static void hi3660_clk_pmuctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_pmu_gate_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_gate(hi3660_pmu_gate_clks,
+ ARRAY_SIZE(hi3660_pmu_gate_clks), clk_data);
+}
+
+static void hi3660_clk_pctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_pctrl_gate_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+ hisi_clk_register_gate(hi3660_pctrl_gate_clks,
+ ARRAY_SIZE(hi3660_pctrl_gate_clks), clk_data);
+}
+
+static void hi3660_clk_sctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_sctrl_gate_clks) +
+ ARRAY_SIZE(hi3660_sctrl_gate_sep_clks) +
+ ARRAY_SIZE(hi3660_sctrl_mux_clks) +
+ ARRAY_SIZE(hi3660_sctrl_divider_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+ hisi_clk_register_gate(hi3660_sctrl_gate_clks,
+ ARRAY_SIZE(hi3660_sctrl_gate_clks), clk_data);
+ hisi_clk_register_gate_sep(hi3660_sctrl_gate_sep_clks,
+ ARRAY_SIZE(hi3660_sctrl_gate_sep_clks),
+ clk_data);
+ hisi_clk_register_mux(hi3660_sctrl_mux_clks,
+ ARRAY_SIZE(hi3660_sctrl_mux_clks), clk_data);
+ hisi_clk_register_divider(hi3660_sctrl_divider_clks,
+ ARRAY_SIZE(hi3660_sctrl_divider_clks),
+ clk_data);
+}
+
+static void hi3660_clk_crgctrl_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+ int nr = ARRAY_SIZE(hi3660_fixed_rate_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_gate_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_mux_clks) +
+ ARRAY_SIZE(hi3660_crg_fixed_factor_clks) +
+ ARRAY_SIZE(hi3660_crgctrl_divider_clks);
+
+ clk_data = hisi_clk_init(np, nr);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_fixed_rate(hi3660_fixed_rate_clks,
+ ARRAY_SIZE(hi3660_fixed_rate_clks),
+ clk_data);
+ hisi_clk_register_gate_sep(hi3660_crgctrl_gate_sep_clks,
+ ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks),
+ clk_data);
+ hisi_clk_register_gate(hi3660_crgctrl_gate_clks,
+ ARRAY_SIZE(hi3660_crgctrl_gate_clks),
+ clk_data);
+ hisi_clk_register_mux(hi3660_crgctrl_mux_clks,
+ ARRAY_SIZE(hi3660_crgctrl_mux_clks),
+ clk_data);
+ hisi_clk_register_fixed_factor(hi3660_crg_fixed_factor_clks,
+ ARRAY_SIZE(hi3660_crg_fixed_factor_clks),
+ clk_data);
+ hisi_clk_register_divider(hi3660_crgctrl_divider_clks,
+ ARRAY_SIZE(hi3660_crgctrl_divider_clks),
+ clk_data);
+}
+
+static const struct of_device_id hi3660_clk_match_table[] = {
+ { .compatible = "hisilicon,hi3660-crgctrl",
+ .data = hi3660_clk_crgctrl_init },
+ { .compatible = "hisilicon,hi3660-pctrl",
+ .data = hi3660_clk_pctrl_init },
+ { .compatible = "hisilicon,hi3660-pmuctrl",
+ .data = hi3660_clk_pmuctrl_init },
+ { .compatible = "hisilicon,hi3660-sctrl",
+ .data = hi3660_clk_sctrl_init },
+ { .compatible = "hisilicon,hi3660-iomcu",
+ .data = hi3660_clk_iomcu_init },
+ { }
+};
+
+static int hi3660_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ void (*init_func)(struct device_node *np);
+
+ init_func = of_device_get_match_data(dev);
+ if (!init_func)
+ return -ENODEV;
+
+ init_func(np);
+
+ return 0;
+}
+
+static struct platform_driver hi3660_clk_driver = {
+ .probe = hi3660_clk_probe,
+ .driver = {
+ .name = "hi3660-clk",
+ .of_match_table = hi3660_clk_match_table,
+ },
+};
+
+static int __init hi3660_clk_init(void)
+{
+ return platform_driver_register(&hi3660_clk_driver);
+}
+core_initcall(hi3660_clk_init);
diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c
index a47812f56a17..7908bc3c9ec7 100644
--- a/drivers/clk/hisilicon/clkgate-separated.c
+++ b/drivers/clk/hisilicon/clkgate-separated.c
@@ -120,6 +120,7 @@ struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
sclk->bit_idx = bit_idx;
sclk->flags = clk_gate_flags;
sclk->hw.init = &init;
+ sclk->lock = lock;
clk = clk_register(dev, &sclk->hw);
if (IS_ERR(clk))
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index 42ffc1c92bab..c07df719b8a3 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -592,15 +592,20 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
imx6q_mmdc_ch1_mask_handshake(base);
- /*
- * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware
- * bug. Set the muxes to the requested values before registering the
- * ldb_di_sel clocks.
- */
- init_ldb_clks(np, base);
+ if (clk_on_imx6qp()) {
+ clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
+ clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
+ } else {
+ /*
+ * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware
+ * bug. Set the muxes to the requested values before registering the
+ * ldb_di_sel clocks.
+ */
+ init_ldb_clks(np, base);
- clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
- clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ }
clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index e7c7353a86fc..ae1d31be906e 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -803,6 +803,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_DRAM_PHYM_ROOT_CLK] = imx_clk_gate4("dram_phym_root_clk", "dram_phym_cg", base + 0x4130, 0);
clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0);
clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0);
+ clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0);
clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0);
clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0);
clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0);
diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
index ed3a2df536ea..f1099167ba31 100644
--- a/drivers/clk/imx/clk-pllv3.c
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -21,6 +21,9 @@
#define PLL_NUM_OFFSET 0x10
#define PLL_DENOM_OFFSET 0x20
+#define PLL_VF610_NUM_OFFSET 0x20
+#define PLL_VF610_DENOM_OFFSET 0x30
+
#define BM_PLL_POWER (0x1 << 12)
#define BM_PLL_LOCK (0x1 << 31)
#define IMX7_ENET_PLL_POWER (0x1 << 5)
@@ -300,6 +303,99 @@ static const struct clk_ops clk_pllv3_av_ops = {
.set_rate = clk_pllv3_av_set_rate,
};
+struct clk_pllv3_vf610_mf {
+ u32 mfi; /* integer part, can be 20 or 22 */
+ u32 mfn; /* numerator, 30-bit value */
+ u32 mfd; /* denominator, 30-bit value, must be less than mfn */
+};
+
+static unsigned long clk_pllv3_vf610_mf_to_rate(unsigned long parent_rate,
+ struct clk_pllv3_vf610_mf mf)
+{
+ u64 temp64;
+
+ temp64 = parent_rate;
+ temp64 *= mf.mfn;
+ do_div(temp64, mf.mfd);
+
+ return (parent_rate * mf.mfi) + temp64;
+}
+
+static struct clk_pllv3_vf610_mf clk_pllv3_vf610_rate_to_mf(
+ unsigned long parent_rate, unsigned long rate)
+{
+ struct clk_pllv3_vf610_mf mf;
+ u64 temp64;
+
+ mf.mfi = (rate >= 22 * parent_rate) ? 22 : 20;
+ mf.mfd = 0x3fffffff; /* use max supported value for best accuracy */
+
+ if (rate <= parent_rate * mf.mfi)
+ mf.mfn = 0;
+ else if (rate >= parent_rate * (mf.mfi + 1))
+ mf.mfn = mf.mfd - 1;
+ else {
+ /* rate = parent_rate * (mfi + mfn/mfd) */
+ temp64 = rate - parent_rate * mf.mfi;
+ temp64 *= mf.mfd;
+ do_div(temp64, parent_rate);
+ mf.mfn = temp64;
+ }
+
+ return mf;
+}
+
+static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pllv3 *pll = to_clk_pllv3(hw);
+ struct clk_pllv3_vf610_mf mf;
+
+ mf.mfn = readl_relaxed(pll->base + PLL_VF610_NUM_OFFSET);
+ mf.mfd = readl_relaxed(pll->base + PLL_VF610_DENOM_OFFSET);
+ mf.mfi = (readl_relaxed(pll->base) & pll->div_mask) ? 22 : 20;
+
+ return clk_pllv3_vf610_mf_to_rate(parent_rate, mf);
+}
+
+static long clk_pllv3_vf610_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(*prate, rate);
+
+ return clk_pllv3_vf610_mf_to_rate(*prate, mf);
+}
+
+static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pllv3 *pll = to_clk_pllv3(hw);
+ struct clk_pllv3_vf610_mf mf =
+ clk_pllv3_vf610_rate_to_mf(parent_rate, rate);
+ u32 val;
+
+ val = readl_relaxed(pll->base);
+ if (mf.mfi == 20)
+ val &= ~pll->div_mask; /* clear bit for mfi=20 */
+ else
+ val |= pll->div_mask; /* set bit for mfi=22 */
+ writel_relaxed(val, pll->base);
+
+ writel_relaxed(mf.mfn, pll->base + PLL_VF610_NUM_OFFSET);
+ writel_relaxed(mf.mfd, pll->base + PLL_VF610_DENOM_OFFSET);
+
+ return clk_pllv3_wait_lock(pll);
+}
+
+static const struct clk_ops clk_pllv3_vf610_ops = {
+ .prepare = clk_pllv3_prepare,
+ .unprepare = clk_pllv3_unprepare,
+ .is_prepared = clk_pllv3_is_prepared,
+ .recalc_rate = clk_pllv3_vf610_recalc_rate,
+ .round_rate = clk_pllv3_vf610_round_rate,
+ .set_rate = clk_pllv3_vf610_set_rate,
+};
+
static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@@ -334,6 +430,9 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
case IMX_PLLV3_SYS:
ops = &clk_pllv3_sys_ops;
break;
+ case IMX_PLLV3_SYS_VF610:
+ ops = &clk_pllv3_vf610_ops;
+ break;
case IMX_PLLV3_USB_VF610:
pll->div_shift = 1;
case IMX_PLLV3_USB:
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 0476353ab423..59b1863deb88 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -219,8 +219,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
- clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1);
- clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1);
+ clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1);
+ clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1);
clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2);
clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f);
clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3);
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 4afad3b96a61..e1f5e425db73 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -34,6 +34,7 @@ enum imx_pllv3_type {
IMX_PLLV3_AV,
IMX_PLLV3_ENET,
IMX_PLLV3_ENET_IMX7,
+ IMX_PLLV3_SYS_VF610,
};
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 0bd631a41f6a..a01ef7806aed 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -8,52 +8,53 @@ config COMMON_CLK_MEDIATEK
config COMMON_CLK_MT2701
bool "Clock driver for Mediatek MT2701"
+ depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
select COMMON_CLK_MEDIATEK
- default ARCH_MEDIATEK
+ default ARCH_MEDIATEK && ARM
---help---
This driver supports Mediatek MT2701 basic clocks.
config COMMON_CLK_MT2701_MMSYS
bool "Clock driver for Mediatek MT2701 mmsys"
- select COMMON_CLK_MT2701
+ depends on COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 mmsys clocks.
config COMMON_CLK_MT2701_IMGSYS
bool "Clock driver for Mediatek MT2701 imgsys"
- select COMMON_CLK_MT2701
+ depends on COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 imgsys clocks.
config COMMON_CLK_MT2701_VDECSYS
bool "Clock driver for Mediatek MT2701 vdecsys"
- select COMMON_CLK_MT2701
+ depends on COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 vdecsys clocks.
config COMMON_CLK_MT2701_HIFSYS
bool "Clock driver for Mediatek MT2701 hifsys"
- select COMMON_CLK_MT2701
+ depends on COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 hifsys clocks.
config COMMON_CLK_MT2701_ETHSYS
bool "Clock driver for Mediatek MT2701 ethsys"
- select COMMON_CLK_MT2701
+ depends on COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 ethsys clocks.
config COMMON_CLK_MT2701_BDPSYS
bool "Clock driver for Mediatek MT2701 bdpsys"
- select COMMON_CLK_MT2701
+ depends on COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 bdpsys clocks.
config COMMON_CLK_MT8135
bool "Clock driver for Mediatek MT8135"
- depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
select COMMON_CLK_MEDIATEK
- default ARCH_MEDIATEK
+ default ARCH_MEDIATEK && ARM
---help---
This driver supports Mediatek MT8135 clocks.
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 3f1be46cbb33..888494d4fb8a 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -607,7 +607,6 @@ static int meson8b_clkc_probe(struct platform_device *pdev)
/* Populate the base address for the MPEG clks */
meson8b_mpeg_clk_sel.reg = clk_base + (u32)meson8b_mpeg_clk_sel.reg;
meson8b_mpeg_clk_div.reg = clk_base + (u32)meson8b_mpeg_clk_div.reg;
- meson8b_clk81.reg = clk_base + (u32)meson8b_clk81.reg;
/* Populate base address for gates */
for (i = 0; i < ARRAY_SIZE(meson8b_clk_gates); i++)
diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c
index 8181b919f062..f17702107ac5 100644
--- a/drivers/clk/mvebu/ap806-system-controller.c
+++ b/drivers/clk/mvebu/ap806-system-controller.c
@@ -55,21 +55,39 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev)
freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
switch (freq_mode) {
- case 0x0 ... 0x5:
+ case 0x0:
+ case 0x1:
cpuclk_freq = 2000;
break;
- case 0x6 ... 0xB:
+ case 0x6:
+ case 0x7:
cpuclk_freq = 1800;
break;
- case 0xC ... 0x11:
+ case 0x4:
+ case 0xB:
+ case 0xD:
cpuclk_freq = 1600;
break;
- case 0x12 ... 0x16:
+ case 0x1a:
cpuclk_freq = 1400;
break;
- case 0x17 ... 0x19:
+ case 0x14:
+ case 0x17:
cpuclk_freq = 1300;
break;
+ case 0x19:
+ cpuclk_freq = 1200;
+ break;
+ case 0x13:
+ case 0x1d:
+ cpuclk_freq = 1000;
+ break;
+ case 0x1c:
+ cpuclk_freq = 800;
+ break;
+ case 0x1b:
+ cpuclk_freq = 600;
+ break;
default:
dev_err(&pdev->dev, "invalid SAR value\n");
return -EINVAL;
diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c
index b3094315a3c0..890a863ae0d0 100644
--- a/drivers/clk/mvebu/armada-xp.c
+++ b/drivers/clk/mvebu/armada-xp.c
@@ -52,6 +52,12 @@ static u32 __init axp_get_tclk_freq(void __iomem *sar)
return 250000000;
}
+/* MV98DX3236 TCLK frequency is fixed to 200MHz */
+static u32 __init mv98dx3236_get_tclk_freq(void __iomem *sar)
+{
+ return 200000000;
+}
+
static const u32 axp_cpu_freqs[] __initconst = {
1000000000,
1066000000,
@@ -89,6 +95,12 @@ static u32 __init axp_get_cpu_freq(void __iomem *sar)
return cpu_freq;
}
+/* MV98DX3236 CLK frequency is fixed to 800MHz */
+static u32 __init mv98dx3236_get_cpu_freq(void __iomem *sar)
+{
+ return 800000000;
+}
+
static const int axp_nbclk_ratios[32][2] __initconst = {
{0, 1}, {1, 2}, {2, 2}, {2, 2},
{1, 2}, {1, 2}, {1, 1}, {2, 3},
@@ -158,6 +170,11 @@ static const struct coreclk_soc_desc axp_coreclks = {
.num_ratios = ARRAY_SIZE(axp_coreclk_ratios),
};
+static const struct coreclk_soc_desc mv98dx3236_coreclks = {
+ .get_tclk_freq = mv98dx3236_get_tclk_freq,
+ .get_cpu_freq = mv98dx3236_get_cpu_freq,
+};
+
/*
* Clock Gating Control
*/
@@ -195,6 +212,15 @@ static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = {
{ }
};
+static const struct clk_gating_soc_desc mv98dx3236_gating_desc[] __initconst = {
+ { "ge1", NULL, 3, 0 },
+ { "ge0", NULL, 4, 0 },
+ { "pex00", NULL, 5, 0 },
+ { "sdio", NULL, 17, 0 },
+ { "xor0", NULL, 22, 0 },
+ { }
+};
+
static void __init axp_clk_init(struct device_node *np)
{
struct device_node *cgnp =
@@ -206,3 +232,16 @@ static void __init axp_clk_init(struct device_node *np)
mvebu_clk_gating_setup(cgnp, axp_gating_desc);
}
CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init);
+
+static void __init mv98dx3236_clk_init(struct device_node *np)
+{
+ struct device_node *cgnp =
+ of_find_compatible_node(NULL, NULL, "marvell,armada-xp-gating-clock");
+
+ mvebu_coreclk_setup(np, &mv98dx3236_coreclks);
+
+ if (cgnp)
+ mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc);
+}
+CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock",
+ mv98dx3236_clk_init);
diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c
index d1e5863d3375..8491979f4096 100644
--- a/drivers/clk/mvebu/clk-corediv.c
+++ b/drivers/clk/mvebu/clk-corediv.c
@@ -71,6 +71,10 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] = {
{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
};
+static const struct clk_corediv_desc mv98dx3236_corediv_desc[] = {
+ { .mask = 0x0f, .offset = 6, .fieldbit = 26 }, /* NAND clock */
+};
+
#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw)
static int clk_corediv_is_enabled(struct clk_hw *hwclk)
@@ -232,6 +236,18 @@ static const struct clk_corediv_soc_desc armada375_corediv_soc = {
.ratio_offset = 0x4,
};
+static const struct clk_corediv_soc_desc mv98dx3236_corediv_soc = {
+ .descs = mv98dx3236_corediv_desc,
+ .ndescs = ARRAY_SIZE(mv98dx3236_corediv_desc),
+ .ops = {
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(10),
+ .ratio_offset = 0x8,
+};
+
static void __init
mvebu_corediv_clk_init(struct device_node *node,
const struct clk_corediv_soc_desc *soc_desc)
@@ -313,3 +329,10 @@ static void __init armada380_corediv_clk_init(struct device_node *node)
}
CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock",
armada380_corediv_clk_init);
+
+static void __init mv98dx3236_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &mv98dx3236_corediv_soc);
+}
+CLK_OF_DECLARE(mv98dx3236_corediv_clk, "marvell,mv98dx3236-corediv-clock",
+ mv98dx3236_corediv_clk_init);
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c
index 5837eb8a212f..044892b6534d 100644
--- a/drivers/clk/mvebu/clk-cpu.c
+++ b/drivers/clk/mvebu/clk-cpu.c
@@ -245,3 +245,11 @@ cpuclk_out:
CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
of_cpu_clk_setup);
+
+static void __init of_mv98dx3236_cpu_clk_setup(struct device_node *node)
+{
+ of_clk_add_provider(node, of_clk_src_simple_get, NULL);
+}
+
+CLK_OF_DECLARE(mv98dx3236_cpu_clock, "marvell,mv98dx3236-cpu-clock",
+ of_mv98dx3236_cpu_clk_setup);
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index 07e2cc6ed781..3487c267833e 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -462,8 +462,79 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
.num_clks = ARRAY_SIZE(msm8916_clks),
};
+/* msm8974 */
+DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
+DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
+DEFINE_CLK_SMD_RPM(msm8974, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
+DEFINE_CLK_SMD_RPM(msm8974, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3);
+DEFINE_CLK_SMD_RPM(msm8974, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
+DEFINE_CLK_SMD_RPM(msm8974, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1);
+DEFINE_CLK_SMD_RPM(msm8974, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2);
+DEFINE_CLK_SMD_RPM_QDSS(msm8974, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d0, cxo_d0_a, 1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d1, cxo_d1_a, 2);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a0, cxo_a0_a, 4);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a1, cxo_a1_a, 5);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a2, cxo_a2_a, 6);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, diff_clk, diff_a_clk, 7);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk1, div_a_clk1, 11);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk2, div_a_clk2, 12);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d0_pin, cxo_d0_a_pin, 1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d1_pin, cxo_d1_a_pin, 2);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a0_pin, cxo_a0_a_pin, 4);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a1_pin, cxo_a1_a_pin, 5);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a2_pin, cxo_a2_a_pin, 6);
+
+static struct clk_smd_rpm *msm8974_clks[] = {
+ [RPM_SMD_PNOC_CLK] = &msm8974_pnoc_clk,
+ [RPM_SMD_PNOC_A_CLK] = &msm8974_pnoc_a_clk,
+ [RPM_SMD_SNOC_CLK] = &msm8974_snoc_clk,
+ [RPM_SMD_SNOC_A_CLK] = &msm8974_snoc_a_clk,
+ [RPM_SMD_CNOC_CLK] = &msm8974_cnoc_clk,
+ [RPM_SMD_CNOC_A_CLK] = &msm8974_cnoc_a_clk,
+ [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8974_mmssnoc_ahb_clk,
+ [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8974_mmssnoc_ahb_a_clk,
+ [RPM_SMD_BIMC_CLK] = &msm8974_bimc_clk,
+ [RPM_SMD_BIMC_A_CLK] = &msm8974_bimc_a_clk,
+ [RPM_SMD_OCMEMGX_CLK] = &msm8974_ocmemgx_clk,
+ [RPM_SMD_OCMEMGX_A_CLK] = &msm8974_ocmemgx_a_clk,
+ [RPM_SMD_QDSS_CLK] = &msm8974_qdss_clk,
+ [RPM_SMD_QDSS_A_CLK] = &msm8974_qdss_a_clk,
+ [RPM_SMD_CXO_D0] = &msm8974_cxo_d0,
+ [RPM_SMD_CXO_D0_A] = &msm8974_cxo_d0_a,
+ [RPM_SMD_CXO_D1] = &msm8974_cxo_d1,
+ [RPM_SMD_CXO_D1_A] = &msm8974_cxo_d1_a,
+ [RPM_SMD_CXO_A0] = &msm8974_cxo_a0,
+ [RPM_SMD_CXO_A0_A] = &msm8974_cxo_a0_a,
+ [RPM_SMD_CXO_A1] = &msm8974_cxo_a1,
+ [RPM_SMD_CXO_A1_A] = &msm8974_cxo_a1_a,
+ [RPM_SMD_CXO_A2] = &msm8974_cxo_a2,
+ [RPM_SMD_CXO_A2_A] = &msm8974_cxo_a2_a,
+ [RPM_SMD_DIFF_CLK] = &msm8974_diff_clk,
+ [RPM_SMD_DIFF_A_CLK] = &msm8974_diff_a_clk,
+ [RPM_SMD_DIV_CLK1] = &msm8974_div_clk1,
+ [RPM_SMD_DIV_A_CLK1] = &msm8974_div_a_clk1,
+ [RPM_SMD_DIV_CLK2] = &msm8974_div_clk2,
+ [RPM_SMD_DIV_A_CLK2] = &msm8974_div_a_clk2,
+ [RPM_SMD_CXO_D0_PIN] = &msm8974_cxo_d0_pin,
+ [RPM_SMD_CXO_D0_A_PIN] = &msm8974_cxo_d0_a_pin,
+ [RPM_SMD_CXO_D1_PIN] = &msm8974_cxo_d1_pin,
+ [RPM_SMD_CXO_D1_A_PIN] = &msm8974_cxo_d1_a_pin,
+ [RPM_SMD_CXO_A0_PIN] = &msm8974_cxo_a0_pin,
+ [RPM_SMD_CXO_A0_A_PIN] = &msm8974_cxo_a0_a_pin,
+ [RPM_SMD_CXO_A1_PIN] = &msm8974_cxo_a1_pin,
+ [RPM_SMD_CXO_A1_A_PIN] = &msm8974_cxo_a1_a_pin,
+ [RPM_SMD_CXO_A2_PIN] = &msm8974_cxo_a2_pin,
+ [RPM_SMD_CXO_A2_A_PIN] = &msm8974_cxo_a2_a_pin,
+};
+
+static const struct rpm_smd_clk_desc rpm_clk_msm8974 = {
+ .clks = msm8974_clks,
+ .num_clks = ARRAY_SIZE(msm8974_clks),
+};
static const struct of_device_id rpm_smd_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
+ { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 },
{ }
};
MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c
index 33d09138f5e5..46cb256b4aa2 100644
--- a/drivers/clk/qcom/gcc-ipq4019.c
+++ b/drivers/clk/qcom/gcc-ipq4019.c
@@ -20,6 +20,9 @@
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
+#include <linux/math64.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
#include <dt-bindings/clock/qcom,gcc-ipq4019.h>
@@ -28,6 +31,13 @@
#include "clk-rcg.h"
#include "clk-branch.h"
#include "reset.h"
+#include "clk-regmap-divider.h"
+
+#define to_clk_regmap_div(_hw) container_of(to_clk_regmap(_hw),\
+ struct clk_regmap_div, clkr)
+
+#define to_clk_fepll(_hw) container_of(to_clk_regmap_div(_hw),\
+ struct clk_fepll, cdiv)
enum {
P_XO,
@@ -40,6 +50,41 @@ enum {
P_DDRPLLAPSS,
};
+/*
+ * struct clk_fepll_vco - vco feedback divider corresponds for FEPLL clocks
+ * @fdbkdiv_shift: lowest bit for FDBKDIV
+ * @fdbkdiv_width: number of bits in FDBKDIV
+ * @refclkdiv_shift: lowest bit for REFCLKDIV
+ * @refclkdiv_width: number of bits in REFCLKDIV
+ * @reg: PLL_DIV register address
+ */
+struct clk_fepll_vco {
+ u32 fdbkdiv_shift;
+ u32 fdbkdiv_width;
+ u32 refclkdiv_shift;
+ u32 refclkdiv_width;
+ u32 reg;
+};
+
+/*
+ * struct clk_fepll - clk divider corresponds to FEPLL clocks
+ * @fixed_div: fixed divider value if divider is fixed
+ * @parent_map: map from software's parent index to hardware's src_sel field
+ * @cdiv: divider values for PLL_DIV
+ * @pll_vco: vco feedback divider
+ * @div_table: mapping for actual divider value to register divider value
+ * in case of non fixed divider
+ * @freq_tbl: frequency table
+ */
+struct clk_fepll {
+ u32 fixed_div;
+ const u8 *parent_map;
+ struct clk_regmap_div cdiv;
+ const struct clk_fepll_vco *pll_vco;
+ const struct clk_div_table *div_table;
+ const struct freq_tbl *freq_tbl;
+};
+
static struct parent_map gcc_xo_200_500_map[] = {
{ P_XO, 0 },
{ P_FEPLL200, 1 },
@@ -80,7 +125,7 @@ static struct parent_map gcc_xo_sdcc1_500_map[] = {
static const char * const gcc_xo_sdcc1_500[] = {
"xo",
- "ddrpll",
+ "ddrpllsdcc",
"fepll500",
};
@@ -121,6 +166,12 @@ static struct parent_map gcc_xo_ddr_500_200_map[] = {
{ P_DDRPLLAPSS, 1 },
};
+/*
+ * Contains index for safe clock during APSS freq change.
+ * fepll500 is being used as safe clock so initialize it
+ * with its index in parents list gcc_xo_ddr_500_200.
+ */
+static const int gcc_ipq4019_cpu_safe_parent = 2;
static const char * const gcc_xo_ddr_500_200[] = {
"xo",
"fepll200",
@@ -505,7 +556,7 @@ static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = {
F(25000000, P_FEPLL500, 1, 1, 20),
F(50000000, P_FEPLL500, 1, 1, 10),
F(100000000, P_FEPLL500, 1, 1, 5),
- F(193000000, P_DDRPLL, 1, 0, 0),
+ F(192000000, P_DDRPLL, 1, 0, 0),
{ }
};
@@ -524,10 +575,20 @@ static struct clk_rcg2 sdcc1_apps_clk_src = {
};
static const struct freq_tbl ftbl_gcc_apps_clk[] = {
- F(48000000, P_XO, 1, 0, 0),
+ F(48000000, P_XO, 1, 0, 0),
F(200000000, P_FEPLL200, 1, 0, 0),
+ F(384000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(413000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(448000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(488000000, P_DDRPLLAPSS, 1, 0, 0),
F(500000000, P_FEPLL500, 1, 0, 0),
- F(626000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(512000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(537000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(565000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(597000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(632000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(672000000, P_DDRPLLAPSS, 1, 0, 0),
+ F(716000000, P_DDRPLLAPSS, 1, 0, 0),
{ }
};
@@ -541,6 +602,7 @@ static struct clk_rcg2 apps_clk_src = {
.parent_names = gcc_xo_ddr_500_200,
.num_parents = 4,
.ops = &clk_rcg2_ops,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -1154,6 +1216,364 @@ static struct clk_branch gcc_wcss5g_rtc_clk = {
},
};
+/* Calculates the VCO rate for FEPLL. */
+static u64 clk_fepll_vco_calc_rate(struct clk_fepll *pll_div,
+ unsigned long parent_rate)
+{
+ const struct clk_fepll_vco *pll_vco = pll_div->pll_vco;
+ u32 fdbkdiv, refclkdiv, cdiv;
+ u64 vco;
+
+ regmap_read(pll_div->cdiv.clkr.regmap, pll_vco->reg, &cdiv);
+ refclkdiv = (cdiv >> pll_vco->refclkdiv_shift) &
+ (BIT(pll_vco->refclkdiv_width) - 1);
+ fdbkdiv = (cdiv >> pll_vco->fdbkdiv_shift) &
+ (BIT(pll_vco->fdbkdiv_width) - 1);
+
+ vco = parent_rate / refclkdiv;
+ vco *= 2;
+ vco *= fdbkdiv;
+
+ return vco;
+}
+
+static const struct clk_fepll_vco gcc_apss_ddrpll_vco = {
+ .fdbkdiv_shift = 16,
+ .fdbkdiv_width = 8,
+ .refclkdiv_shift = 24,
+ .refclkdiv_width = 5,
+ .reg = 0x2e020,
+};
+
+static const struct clk_fepll_vco gcc_fepll_vco = {
+ .fdbkdiv_shift = 16,
+ .fdbkdiv_width = 8,
+ .refclkdiv_shift = 24,
+ .refclkdiv_width = 5,
+ .reg = 0x2f020,
+};
+
+/*
+ * Round rate function for APSS CPU PLL Clock divider.
+ * It looks up the frequency table and returns the next higher frequency
+ * supported in hardware.
+ */
+static long clk_cpu_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate)
+{
+ struct clk_fepll *pll = to_clk_fepll(hw);
+ struct clk_hw *p_hw;
+ const struct freq_tbl *f;
+
+ f = qcom_find_freq(pll->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ p_hw = clk_hw_get_parent_by_index(hw, f->src);
+ *p_rate = clk_hw_get_rate(p_hw);
+
+ return f->freq;
+};
+
+/*
+ * Clock set rate function for APSS CPU PLL Clock divider.
+ * It looks up the frequency table and updates the PLL divider to corresponding
+ * divider value.
+ */
+static int clk_cpu_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_fepll *pll = to_clk_fepll(hw);
+ const struct freq_tbl *f;
+ u32 mask;
+ int ret;
+
+ f = qcom_find_freq(pll->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ mask = (BIT(pll->cdiv.width) - 1) << pll->cdiv.shift;
+ ret = regmap_update_bits(pll->cdiv.clkr.regmap,
+ pll->cdiv.reg, mask,
+ f->pre_div << pll->cdiv.shift);
+ /*
+ * There is no status bit which can be checked for successful CPU
+ * divider update operation so using delay for the same.
+ */
+ udelay(1);
+
+ return 0;
+};
+
+/*
+ * Clock frequency calculation function for APSS CPU PLL Clock divider.
+ * This clock divider is nonlinear so this function calculates the actual
+ * divider and returns the output frequency by dividing VCO Frequency
+ * with this actual divider value.
+ */
+static unsigned long
+clk_cpu_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_fepll *pll = to_clk_fepll(hw);
+ u32 cdiv, pre_div;
+ u64 rate;
+
+ regmap_read(pll->cdiv.clkr.regmap, pll->cdiv.reg, &cdiv);
+ cdiv = (cdiv >> pll->cdiv.shift) & (BIT(pll->cdiv.width) - 1);
+
+ /*
+ * Some dividers have value in 0.5 fraction so multiply both VCO
+ * frequency(parent_rate) and pre_div with 2 to make integer
+ * calculation.
+ */
+ if (cdiv > 10)
+ pre_div = (cdiv + 1) * 2;
+ else
+ pre_div = cdiv + 12;
+
+ rate = clk_fepll_vco_calc_rate(pll, parent_rate) * 2;
+ do_div(rate, pre_div);
+
+ return rate;
+};
+
+static const struct clk_ops clk_regmap_cpu_div_ops = {
+ .round_rate = clk_cpu_div_round_rate,
+ .set_rate = clk_cpu_div_set_rate,
+ .recalc_rate = clk_cpu_div_recalc_rate,
+};
+
+static const struct freq_tbl ftbl_apss_ddr_pll[] = {
+ { 384000000, P_XO, 0xd, 0, 0 },
+ { 413000000, P_XO, 0xc, 0, 0 },
+ { 448000000, P_XO, 0xb, 0, 0 },
+ { 488000000, P_XO, 0xa, 0, 0 },
+ { 512000000, P_XO, 0x9, 0, 0 },
+ { 537000000, P_XO, 0x8, 0, 0 },
+ { 565000000, P_XO, 0x7, 0, 0 },
+ { 597000000, P_XO, 0x6, 0, 0 },
+ { 632000000, P_XO, 0x5, 0, 0 },
+ { 672000000, P_XO, 0x4, 0, 0 },
+ { 716000000, P_XO, 0x3, 0, 0 },
+ { 768000000, P_XO, 0x2, 0, 0 },
+ { 823000000, P_XO, 0x1, 0, 0 },
+ { 896000000, P_XO, 0x0, 0, 0 },
+ { }
+};
+
+static struct clk_fepll gcc_apss_cpu_plldiv_clk = {
+ .cdiv.reg = 0x2e020,
+ .cdiv.shift = 4,
+ .cdiv.width = 4,
+ .cdiv.clkr = {
+ .enable_reg = 0x2e000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "ddrpllapss",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_regmap_cpu_div_ops,
+ },
+ },
+ .freq_tbl = ftbl_apss_ddr_pll,
+ .pll_vco = &gcc_apss_ddrpll_vco,
+};
+
+/* Calculates the rate for PLL divider.
+ * If the divider value is not fixed then it gets the actual divider value
+ * from divider table. Then, it calculate the clock rate by dividing the
+ * parent rate with actual divider value.
+ */
+static unsigned long
+clk_regmap_clk_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_fepll *pll = to_clk_fepll(hw);
+ u32 cdiv, pre_div = 1;
+ u64 rate;
+ const struct clk_div_table *clkt;
+
+ if (pll->fixed_div) {
+ pre_div = pll->fixed_div;
+ } else {
+ regmap_read(pll->cdiv.clkr.regmap, pll->cdiv.reg, &cdiv);
+ cdiv = (cdiv >> pll->cdiv.shift) & (BIT(pll->cdiv.width) - 1);
+
+ for (clkt = pll->div_table; clkt->div; clkt++) {
+ if (clkt->val == cdiv)
+ pre_div = clkt->div;
+ }
+ }
+
+ rate = clk_fepll_vco_calc_rate(pll, parent_rate);
+ do_div(rate, pre_div);
+
+ return rate;
+};
+
+static const struct clk_ops clk_fepll_div_ops = {
+ .recalc_rate = clk_regmap_clk_div_recalc_rate,
+};
+
+static struct clk_fepll gcc_apss_sdcc_clk = {
+ .fixed_div = 28,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "ddrpllsdcc",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .pll_vco = &gcc_apss_ddrpll_vco,
+};
+
+static struct clk_fepll gcc_fepll125_clk = {
+ .fixed_div = 32,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "fepll125",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .pll_vco = &gcc_fepll_vco,
+};
+
+static struct clk_fepll gcc_fepll125dly_clk = {
+ .fixed_div = 32,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "fepll125dly",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .pll_vco = &gcc_fepll_vco,
+};
+
+static struct clk_fepll gcc_fepll200_clk = {
+ .fixed_div = 20,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "fepll200",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .pll_vco = &gcc_fepll_vco,
+};
+
+static struct clk_fepll gcc_fepll500_clk = {
+ .fixed_div = 8,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "fepll500",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .pll_vco = &gcc_fepll_vco,
+};
+
+static const struct clk_div_table fepllwcss_clk_div_table[] = {
+ { 0, 15 },
+ { 1, 16 },
+ { 2, 18 },
+ { 3, 20 },
+ { },
+};
+
+static struct clk_fepll gcc_fepllwcss2g_clk = {
+ .cdiv.reg = 0x2f020,
+ .cdiv.shift = 8,
+ .cdiv.width = 2,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "fepllwcss2g",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .div_table = fepllwcss_clk_div_table,
+ .pll_vco = &gcc_fepll_vco,
+};
+
+static struct clk_fepll gcc_fepllwcss5g_clk = {
+ .cdiv.reg = 0x2f020,
+ .cdiv.shift = 12,
+ .cdiv.width = 2,
+ .cdiv.clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "fepllwcss5g",
+ .parent_names = (const char *[]){
+ "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_fepll_div_ops,
+ },
+ },
+ .div_table = fepllwcss_clk_div_table,
+ .pll_vco = &gcc_fepll_vco,
+};
+
+static const struct freq_tbl ftbl_gcc_pcnoc_ahb_clk[] = {
+ F(48000000, P_XO, 1, 0, 0),
+ F(100000000, P_FEPLL200, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 gcc_pcnoc_ahb_clk_src = {
+ .cmd_rcgr = 0x21024,
+ .hid_width = 5,
+ .parent_map = gcc_xo_200_500_map,
+ .freq_tbl = ftbl_gcc_pcnoc_ahb_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gcc_pcnoc_ahb_clk_src",
+ .parent_names = gcc_xo_200_500,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_branch pcnoc_clk_src = {
+ .halt_reg = 0x21030,
+ .clkr = {
+ .enable_reg = 0x21030,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "pcnoc_clk_src",
+ .parent_names = (const char *[]){
+ "gcc_pcnoc_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ .flags = CLK_SET_RATE_PARENT |
+ CLK_IS_CRITICAL,
+ },
+ },
+};
+
static struct clk_regmap *gcc_ipq4019_clocks[] = {
[AUDIO_CLK_SRC] = &audio_clk_src.clkr,
[BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr,
@@ -1214,6 +1634,16 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = {
[GCC_WCSS5G_CLK] = &gcc_wcss5g_clk.clkr,
[GCC_WCSS5G_REF_CLK] = &gcc_wcss5g_ref_clk.clkr,
[GCC_WCSS5G_RTC_CLK] = &gcc_wcss5g_rtc_clk.clkr,
+ [GCC_SDCC_PLLDIV_CLK] = &gcc_apss_sdcc_clk.cdiv.clkr,
+ [GCC_FEPLL125_CLK] = &gcc_fepll125_clk.cdiv.clkr,
+ [GCC_FEPLL125DLY_CLK] = &gcc_fepll125dly_clk.cdiv.clkr,
+ [GCC_FEPLL200_CLK] = &gcc_fepll200_clk.cdiv.clkr,
+ [GCC_FEPLL500_CLK] = &gcc_fepll500_clk.cdiv.clkr,
+ [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr,
+ [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr,
+ [GCC_APSS_CPU_PLLDIV_CLK] = &gcc_apss_cpu_plldiv_clk.cdiv.clkr,
+ [GCC_PCNOC_AHB_CLK_SRC] = &gcc_pcnoc_ahb_clk_src.clkr,
+ [GCC_PCNOC_AHB_CLK] = &pcnoc_clk_src.clkr,
};
static const struct qcom_reset_map gcc_ipq4019_resets[] = {
@@ -1294,7 +1724,7 @@ static const struct regmap_config gcc_ipq4019_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0x2dfff,
+ .max_register = 0x2ffff,
.fast_io = true,
};
@@ -1312,23 +1742,44 @@ static const struct of_device_id gcc_ipq4019_match_table[] = {
};
MODULE_DEVICE_TABLE(of, gcc_ipq4019_match_table);
+static int
+gcc_ipq4019_cpu_clk_notifier_fn(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int err = 0;
+
+ if (action == PRE_RATE_CHANGE)
+ err = clk_rcg2_ops.set_parent(&apps_clk_src.clkr.hw,
+ gcc_ipq4019_cpu_safe_parent);
+
+ return notifier_from_errno(err);
+}
+
+static struct notifier_block gcc_ipq4019_cpu_clk_notifier = {
+ .notifier_call = gcc_ipq4019_cpu_clk_notifier_fn,
+};
+
static int gcc_ipq4019_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
+ int err;
- clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000);
- clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000);
- clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000);
- clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000);
- clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000);
- clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000);
- clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000);
+ err = qcom_cc_probe(pdev, &gcc_ipq4019_desc);
+ if (err)
+ return err;
- return qcom_cc_probe(pdev, &gcc_ipq4019_desc);
+ return clk_notifier_register(apps_clk_src.clkr.hw.clk,
+ &gcc_ipq4019_cpu_clk_notifier);
+}
+
+static int gcc_ipq4019_remove(struct platform_device *pdev)
+{
+ return clk_notifier_unregister(apps_clk_src.clkr.hw.clk,
+ &gcc_ipq4019_cpu_clk_notifier);
}
static struct platform_driver gcc_ipq4019_driver = {
.probe = gcc_ipq4019_probe,
+ .remove = gcc_ipq4019_remove,
.driver = {
.name = "qcom,gcc-ipq4019",
.of_match_table = gcc_ipq4019_match_table,
diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c
index 581a17f67379..b99dd406e907 100644
--- a/drivers/clk/qcom/gcc-mdm9615.c
+++ b/drivers/clk/qcom/gcc-mdm9615.c
@@ -1563,6 +1563,34 @@ static struct clk_branch rpm_msg_ram_h_clk = {
},
};
+static struct clk_branch ebi2_clk = {
+ .hwcg_reg = 0x2664,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fcc,
+ .halt_bit = 24,
+ .clkr = {
+ .enable_reg = 0x2664,
+ .enable_mask = BIT(6) | BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "ebi2_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch ebi2_aon_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 23,
+ .clkr = {
+ .enable_reg = 0x2664,
+ .enable_mask = BIT(8),
+ .hw.init = &(struct clk_init_data){
+ .name = "ebi2_aon_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
static struct clk_hw *gcc_mdm9615_hws[] = {
&cxo.hw,
};
@@ -1637,6 +1665,8 @@ static struct clk_regmap *gcc_mdm9615_clks[] = {
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
+ [EBI2_CLK] = &ebi2_clk.clkr,
+ [EBI2_AON_CLK] = &ebi2_aon_clk.clkr,
};
static const struct qcom_reset_map gcc_mdm9615_resets[] = {
diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c
index 8afd8304a070..7983288d9141 100644
--- a/drivers/clk/qcom/gcc-msm8994.c
+++ b/drivers/clk/qcom/gcc-msm8994.c
@@ -1888,6 +1888,23 @@ static struct clk_branch gcc_sdcc1_apps_clk = {
},
};
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+ .halt_reg = 0x04c8,
+ .clkr = {
+ .enable_reg = 0x04c8,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data)
+ {
+ .name = "gcc_sdcc1_ahb_clk",
+ .parent_names = (const char *[]){
+ "periph_noc_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_branch gcc_sdcc2_apps_clk = {
.halt_reg = 0x0504,
.clkr = {
@@ -2231,6 +2248,7 @@ static struct clk_regmap *gcc_msm8994_clocks[] = {
[GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr,
[GCC_SDCC3_APPS_CLK] = &gcc_sdcc3_apps_clk.clkr,
[GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr,
+ [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr,
[GCC_SYS_NOC_UFS_AXI_CLK] = &gcc_sys_noc_ufs_axi_clk.clkr,
[GCC_SYS_NOC_USB3_AXI_CLK] = &gcc_sys_noc_usb3_axi_clk.clkr,
[GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr,
diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c
index 4b1fc1730d29..8abc200d4fd3 100644
--- a/drivers/clk/qcom/gcc-msm8996.c
+++ b/drivers/clk/qcom/gcc-msm8996.c
@@ -3448,6 +3448,7 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = {
[GCC_MSMPU_BCR] = { 0x8d000 },
[GCC_MSS_Q6_BCR] = { 0x8e000 },
[GCC_QREFS_VBG_CAL_BCR] = { 0x88020 },
+ [GCC_MSS_RESTART] = { 0x8f008 },
};
static const struct regmap_config gcc_msm8996_regmap_config = {
diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index 288186cce0ae..a4f3580587b7 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -63,11 +63,26 @@ static int gdsc_hwctrl(struct gdsc *sc, bool en)
return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val);
}
+static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en)
+{
+ ktime_t start;
+
+ start = ktime_get();
+ do {
+ if (gdsc_is_enabled(sc, reg) == en)
+ return 0;
+ } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US);
+
+ if (gdsc_is_enabled(sc, reg) == en)
+ return 0;
+
+ return -ETIMEDOUT;
+}
+
static int gdsc_toggle_logic(struct gdsc *sc, bool en)
{
int ret;
u32 val = en ? 0 : SW_COLLAPSE_MASK;
- ktime_t start;
unsigned int status_reg = sc->gdscr;
ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val);
@@ -100,16 +115,7 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en)
udelay(1);
}
- start = ktime_get();
- do {
- if (gdsc_is_enabled(sc, status_reg) == en)
- return 0;
- } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US);
-
- if (gdsc_is_enabled(sc, status_reg) == en)
- return 0;
-
- return -ETIMEDOUT;
+ return gdsc_poll_status(sc, status_reg, en);
}
static inline int gdsc_deassert_reset(struct gdsc *sc)
@@ -188,8 +194,20 @@ static int gdsc_enable(struct generic_pm_domain *domain)
udelay(1);
/* Turn on HW trigger mode if supported */
- if (sc->flags & HW_CTRL)
- return gdsc_hwctrl(sc, true);
+ if (sc->flags & HW_CTRL) {
+ ret = gdsc_hwctrl(sc, true);
+ if (ret)
+ return ret;
+ /*
+ * Wait for the GDSC to go through a power down and
+ * up cycle. In case a firmware ends up polling status
+ * bits for the gdsc, it might read an 'on' status before
+ * the GDSC can finish the power cycle.
+ * We wait 1us before returning to ensure the firmware
+ * can't immediately poll the status bits.
+ */
+ udelay(1);
+ }
return 0;
}
@@ -204,9 +222,23 @@ static int gdsc_disable(struct generic_pm_domain *domain)
/* Turn off HW trigger mode if supported */
if (sc->flags & HW_CTRL) {
+ unsigned int reg;
+
ret = gdsc_hwctrl(sc, false);
if (ret < 0)
return ret;
+ /*
+ * Wait for the GDSC to go through a power down and
+ * up cycle. In case we end up polling status
+ * bits for the gdsc before the power cycle is completed
+ * it might read an 'on' status wrongly.
+ */
+ udelay(1);
+
+ reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
+ ret = gdsc_poll_status(sc, reg, true);
+ if (ret)
+ return ret;
}
if (sc->pwrsts & PWRSTS_OFF)
diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c
index 9375777776d9..3ce819c26077 100644
--- a/drivers/clk/renesas/clk-mstp.c
+++ b/drivers/clk/renesas/clk-mstp.c
@@ -37,12 +37,14 @@
* @smstpcr: module stop control register
* @mstpsr: module stop status register (optional)
* @lock: protects writes to SMSTPCR
+ * @width_8bit: registers are 8-bit, not 32-bit
*/
struct mstp_clock_group {
struct clk_onecell_data data;
void __iomem *smstpcr;
void __iomem *mstpsr;
spinlock_t lock;
+ bool width_8bit;
};
/**
@@ -59,6 +61,18 @@ struct mstp_clock {
#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
+static inline u32 cpg_mstp_read(struct mstp_clock_group *group,
+ u32 __iomem *reg)
+{
+ return group->width_8bit ? readb(reg) : clk_readl(reg);
+}
+
+static inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val,
+ u32 __iomem *reg)
+{
+ group->width_8bit ? writeb(val, reg) : clk_writel(val, reg);
+}
+
static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
{
struct mstp_clock *clock = to_mstp_clock(hw);
@@ -70,12 +84,12 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
spin_lock_irqsave(&group->lock, flags);
- value = clk_readl(group->smstpcr);
+ value = cpg_mstp_read(group, group->smstpcr);
if (enable)
value &= ~bitmask;
else
value |= bitmask;
- clk_writel(value, group->smstpcr);
+ cpg_mstp_write(group, value, group->smstpcr);
spin_unlock_irqrestore(&group->lock, flags);
@@ -83,7 +97,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
return 0;
for (i = 1000; i > 0; --i) {
- if (!(clk_readl(group->mstpsr) & bitmask))
+ if (!(cpg_mstp_read(group, group->mstpsr) & bitmask))
break;
cpu_relax();
}
@@ -114,9 +128,9 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw)
u32 value;
if (group->mstpsr)
- value = clk_readl(group->mstpsr);
+ value = cpg_mstp_read(group, group->mstpsr);
else
- value = clk_readl(group->smstpcr);
+ value = cpg_mstp_read(group, group->smstpcr);
return !(value & BIT(clock->bit_index));
}
@@ -127,9 +141,9 @@ static const struct clk_ops cpg_mstp_clock_ops = {
.is_enabled = cpg_mstp_clock_is_enabled,
};
-static struct clk * __init
-cpg_mstp_clock_register(const char *name, const char *parent_name,
- unsigned int index, struct mstp_clock_group *group)
+static struct clk * __init cpg_mstp_clock_register(const char *name,
+ const char *parent_name, unsigned int index,
+ struct mstp_clock_group *group)
{
struct clk_init_data init;
struct mstp_clock *clock;
@@ -144,6 +158,11 @@ cpg_mstp_clock_register(const char *name, const char *parent_name,
init.name = name;
init.ops = &cpg_mstp_clock_ops;
init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
+ /* INTC-SYS is the module clock of the GIC, and must not be disabled */
+ if (!strcmp(name, "intc-sys")) {
+ pr_debug("MSTP %s setting CLK_IS_CRITICAL\n", name);
+ init.flags |= CLK_IS_CRITICAL;
+ }
init.parent_names = &parent_name;
init.num_parents = 1;
@@ -188,6 +207,9 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
return;
}
+ if (of_device_is_compatible(np, "renesas,r7s72100-mstp-clocks"))
+ group->width_8bit = true;
+
for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
clks[i] = ERR_PTR(-ENOENT);
diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c
index 50698a7d9074..bfffdb00df97 100644
--- a/drivers/clk/renesas/r8a7795-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c
@@ -221,6 +221,7 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = {
DEF_MOD("can-if0", 916, R8A7795_CLK_S3D4),
DEF_MOD("i2c6", 918, R8A7795_CLK_S3D2),
DEF_MOD("i2c5", 919, R8A7795_CLK_S3D2),
+ DEF_MOD("i2c-dvfs", 926, R8A7795_CLK_CP),
DEF_MOD("i2c4", 927, R8A7795_CLK_S3D2),
DEF_MOD("i2c3", 928, R8A7795_CLK_S3D2),
DEF_MOD("i2c2", 929, R8A7795_CLK_S3D2),
diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c
index 7d298c57a3e0..11e084a56b0d 100644
--- a/drivers/clk/renesas/r8a7796-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c
@@ -103,7 +103,9 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = {
DEF_FIXED("cl", R8A7796_CLK_CL, CLK_PLL1_DIV2, 48, 1),
DEF_FIXED("cp", R8A7796_CLK_CP, CLK_EXTAL, 2, 1),
+ DEF_DIV6P1("canfd", R8A7796_CLK_CANFD, CLK_PLL1_DIV4, 0x244),
DEF_DIV6P1("csi0", R8A7796_CLK_CSI0, CLK_PLL1_DIV4, 0x00c),
+ DEF_DIV6P1("mso", R8A7796_CLK_MSO, CLK_PLL1_DIV4, 0x014),
DEF_DIV6_RO("osc", R8A7796_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8),
DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32),
@@ -117,6 +119,10 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
DEF_MOD("scif3", 204, R8A7796_CLK_S3D4),
DEF_MOD("scif1", 206, R8A7796_CLK_S3D4),
DEF_MOD("scif0", 207, R8A7796_CLK_S3D4),
+ DEF_MOD("msiof3", 208, R8A7796_CLK_MSO),
+ DEF_MOD("msiof2", 209, R8A7796_CLK_MSO),
+ DEF_MOD("msiof1", 210, R8A7796_CLK_MSO),
+ DEF_MOD("msiof0", 211, R8A7796_CLK_MSO),
DEF_MOD("sys-dmac2", 217, R8A7796_CLK_S0D3),
DEF_MOD("sys-dmac1", 218, R8A7796_CLK_S0D3),
DEF_MOD("sys-dmac0", 219, R8A7796_CLK_S0D3),
@@ -181,8 +187,12 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
DEF_MOD("gpio2", 910, R8A7796_CLK_S3D4),
DEF_MOD("gpio1", 911, R8A7796_CLK_S3D4),
DEF_MOD("gpio0", 912, R8A7796_CLK_S3D4),
+ DEF_MOD("can-fd", 914, R8A7796_CLK_S3D2),
+ DEF_MOD("can-if1", 915, R8A7796_CLK_S3D4),
+ DEF_MOD("can-if0", 916, R8A7796_CLK_S3D4),
DEF_MOD("i2c6", 918, R8A7796_CLK_S0D6),
DEF_MOD("i2c5", 919, R8A7796_CLK_S0D6),
+ DEF_MOD("i2c-dvfs", 926, R8A7796_CLK_CP),
DEF_MOD("i2c4", 927, R8A7796_CLK_S0D6),
DEF_MOD("i2c3", 928, R8A7796_CLK_S0D6),
DEF_MOD("i2c2", 929, R8A7796_CLK_S3D2),
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 8359ce75db7a..eadcbd43ff88 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -16,6 +16,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clk/renesas.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
@@ -25,6 +26,7 @@
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
+#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <dt-bindings/clock/renesas-cpg-mssr.h>
@@ -43,7 +45,7 @@
* Module Standby and Software Reset register offets.
*
* If the registers exist, these are valid for SH-Mobile, R-Mobile,
- * R-Car Gen 2, and R-Car Gen 3.
+ * R-Car Gen2, R-Car Gen3, and RZ/G1.
* These are NOT valid for R-Car Gen1 and RZ/A1!
*/
@@ -96,18 +98,22 @@ static const u16 srcr[] = {
/**
* Clock Pulse Generator / Module Standby and Software Reset Private Data
*
+ * @rcdev: Optional reset controller entity
* @dev: CPG/MSSR device
* @base: CPG/MSSR register block base address
- * @mstp_lock: protects writes to SMSTPCR
+ * @rmw_lock: protects RMW register accesses
* @clks: Array containing all Core and Module Clocks
* @num_core_clks: Number of Core Clocks in clks[]
* @num_mod_clks: Number of Module Clocks in clks[]
* @last_dt_core_clk: ID of the last Core Clock exported to DT
*/
struct cpg_mssr_priv {
+#ifdef CONFIG_RESET_CONTROLLER
+ struct reset_controller_dev rcdev;
+#endif
struct device *dev;
void __iomem *base;
- spinlock_t mstp_lock;
+ spinlock_t rmw_lock;
struct clk **clks;
unsigned int num_core_clks;
@@ -144,7 +150,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk,
enable ? "ON" : "OFF");
- spin_lock_irqsave(&priv->mstp_lock, flags);
+ spin_lock_irqsave(&priv->rmw_lock, flags);
value = readl(priv->base + SMSTPCR(reg));
if (enable)
@@ -153,7 +159,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
value |= bitmask;
writel(value, priv->base + SMSTPCR(reg));
- spin_unlock_irqrestore(&priv->mstp_lock, flags);
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
if (!enable)
return 0;
@@ -346,17 +352,10 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod,
init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
for (i = 0; i < info->num_crit_mod_clks; i++)
if (id == info->crit_mod_clks[i]) {
-#ifdef CLK_ENABLE_HAND_OFF
- dev_dbg(dev, "MSTP %s setting CLK_ENABLE_HAND_OFF\n",
+ dev_dbg(dev, "MSTP %s setting CLK_IS_CRITICAL\n",
mod->name);
- init.flags |= CLK_ENABLE_HAND_OFF;
+ init.flags |= CLK_IS_CRITICAL;
break;
-#else
- dev_dbg(dev, "Ignoring MSTP %s to prevent disabling\n",
- mod->name);
- kfree(clock);
- return;
-#endif
}
parent_name = __clk_get_name(parent);
@@ -501,6 +500,122 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev,
return 0;
}
+#ifdef CONFIG_RESET_CONTROLLER
+
+#define rcdev_to_priv(x) container_of(x, struct cpg_mssr_priv, rcdev)
+
+static int cpg_mssr_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+ u32 bitmask = BIT(bit);
+ unsigned long flags;
+ u32 value;
+
+ dev_dbg(priv->dev, "reset %u%02u\n", reg, bit);
+
+ /* Reset module */
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+ value = readl(priv->base + SRCR(reg));
+ value |= bitmask;
+ writel(value, priv->base + SRCR(reg));
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+
+ /* Wait for at least one cycle of the RCLK clock (@ ca. 32 kHz) */
+ udelay(35);
+
+ /* Release module from reset state */
+ writel(bitmask, priv->base + SRSTCLR(reg));
+
+ return 0;
+}
+
+static int cpg_mssr_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+ u32 bitmask = BIT(bit);
+ unsigned long flags;
+ u32 value;
+
+ dev_dbg(priv->dev, "assert %u%02u\n", reg, bit);
+
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+ value = readl(priv->base + SRCR(reg));
+ value |= bitmask;
+ writel(value, priv->base + SRCR(reg));
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+ return 0;
+}
+
+static int cpg_mssr_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+ u32 bitmask = BIT(bit);
+
+ dev_dbg(priv->dev, "deassert %u%02u\n", reg, bit);
+
+ writel(bitmask, priv->base + SRSTCLR(reg));
+ return 0;
+}
+
+static int cpg_mssr_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+ u32 bitmask = BIT(bit);
+
+ return !!(readl(priv->base + SRCR(reg)) & bitmask);
+}
+
+static const struct reset_control_ops cpg_mssr_reset_ops = {
+ .reset = cpg_mssr_reset,
+ .assert = cpg_mssr_assert,
+ .deassert = cpg_mssr_deassert,
+ .status = cpg_mssr_status,
+};
+
+static int cpg_mssr_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int unpacked = reset_spec->args[0];
+ unsigned int idx = MOD_CLK_PACK(unpacked);
+
+ if (unpacked % 100 > 31 || idx >= rcdev->nr_resets) {
+ dev_err(priv->dev, "Invalid reset index %u\n", unpacked);
+ return -EINVAL;
+ }
+
+ return idx;
+}
+
+static int cpg_mssr_reset_controller_register(struct cpg_mssr_priv *priv)
+{
+ priv->rcdev.ops = &cpg_mssr_reset_ops;
+ priv->rcdev.of_node = priv->dev->of_node;
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.of_xlate = cpg_mssr_reset_xlate;
+ priv->rcdev.nr_resets = priv->num_mod_clks;
+ return devm_reset_controller_register(priv->dev, &priv->rcdev);
+}
+
+#else /* !CONFIG_RESET_CONTROLLER */
+static inline int cpg_mssr_reset_controller_register(struct cpg_mssr_priv *priv)
+{
+ return 0;
+}
+#endif /* !CONFIG_RESET_CONTROLLER */
+
+
static const struct of_device_id cpg_mssr_match[] = {
#ifdef CONFIG_ARCH_R8A7743
{
@@ -557,7 +672,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dev = dev;
- spin_lock_init(&priv->mstp_lock);
+ spin_lock_init(&priv->rmw_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
@@ -598,6 +713,10 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
if (error)
return error;
+ error = cpg_mssr_reset_controller_register(priv);
+ if (error)
+ return error;
+
return 0;
}
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index 57f4dc6dc447..7afc21dc374e 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -5,7 +5,6 @@
obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o
obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o
obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o
-obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o
obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o
obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index 17e68a724945..cb7df358a27d 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -44,7 +44,7 @@ static unsigned long reg_save[][2] = {
{ ASS_CLK_GATE, 0 },
};
-static int exynos_audss_clk_suspend(void)
+static int exynos_audss_clk_suspend(struct device *dev)
{
int i;
@@ -54,18 +54,15 @@ static int exynos_audss_clk_suspend(void)
return 0;
}
-static void exynos_audss_clk_resume(void)
+static int exynos_audss_clk_resume(struct device *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(reg_save); i++)
writel(reg_save[i][1], reg_base + reg_save[i][0]);
-}
-static struct syscore_ops exynos_audss_clk_syscore_ops = {
- .suspend = exynos_audss_clk_suspend,
- .resume = exynos_audss_clk_resume,
-};
+ return 0;
+}
#endif /* CONFIG_PM_SLEEP */
struct exynos_audss_clk_drvdata {
@@ -251,9 +248,6 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
goto unregister;
}
-#ifdef CONFIG_PM_SLEEP
- register_syscore_ops(&exynos_audss_clk_syscore_ops);
-#endif
return 0;
unregister:
@@ -267,10 +261,6 @@ unregister:
static int exynos_audss_clk_remove(struct platform_device *pdev)
{
-#ifdef CONFIG_PM_SLEEP
- unregister_syscore_ops(&exynos_audss_clk_syscore_ops);
-#endif
-
of_clk_del_provider(pdev->dev.of_node);
exynos_audss_clk_teardown();
@@ -281,10 +271,16 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
return 0;
}
+static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
+ exynos_audss_clk_resume)
+};
+
static struct platform_driver exynos_audss_clk_driver = {
.driver = {
.name = "exynos-audss-clk",
.of_match_table = exynos_audss_clk_of_match,
+ .pm = &exynos_audss_clk_pm_ops,
},
.probe = exynos_audss_clk_probe,
.remove = exynos_audss_clk_remove,
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index faab9b31baf5..e40b77583c47 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -1298,6 +1298,8 @@ static const struct samsung_pll_rate_table exynos4210_vpll_rates[] __initconst =
};
static const struct samsung_pll_rate_table exynos4x12_apll_rates[] __initconst = {
+ PLL_35XX_RATE(1704000000, 213, 3, 0),
+ PLL_35XX_RATE(1600000000, 200, 3, 0),
PLL_35XX_RATE(1500000000, 250, 4, 0),
PLL_35XX_RATE(1400000000, 175, 3, 0),
PLL_35XX_RATE(1300000000, 325, 6, 0),
@@ -1421,6 +1423,8 @@ static const struct exynos_cpuclk_cfg_data e4212_armclk_d[] __initconst = {
(((cores) << 8) | ((hpm) << 4) | ((copy) << 0))
static const struct exynos_cpuclk_cfg_data e4412_armclk_d[] __initconst = {
+ { 1704000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(7, 0, 7), },
+ { 1600000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(7, 0, 6), },
{ 1500000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(7, 0, 6), },
{ 1400000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(6, 0, 6), },
{ 1300000, E4210_CPU_DIV0(2, 1, 5, 0, 7, 3), E4412_CPU_DIV1(6, 0, 5), },
diff --git a/drivers/clk/samsung/clk-exynos4415.c b/drivers/clk/samsung/clk-exynos4415.c
deleted file mode 100644
index 6c9063159717..000000000000
--- a/drivers/clk/samsung/clk-exynos4415.c
+++ /dev/null
@@ -1,1022 +0,0 @@
-/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
- * Author: Chanwoo Choi <cw00.choi@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Common Clock Framework support for Exynos4415 SoC.
- */
-
-#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/platform_device.h>
-#include <linux/syscore_ops.h>
-
-#include <dt-bindings/clock/exynos4415.h>
-
-#include "clk.h"
-#include "clk-pll.h"
-
-#define SRC_LEFTBUS 0x4200
-#define DIV_LEFTBUS 0x4500
-#define GATE_IP_LEFTBUS 0x4800
-#define GATE_IP_IMAGE 0x4930
-#define SRC_RIGHTBUS 0x8200
-#define DIV_RIGHTBUS 0x8500
-#define GATE_IP_RIGHTBUS 0x8800
-#define GATE_IP_PERIR 0x8960
-#define EPLL_LOCK 0xc010
-#define G3D_PLL_LOCK 0xc020
-#define DISP_PLL_LOCK 0xc030
-#define ISP_PLL_LOCK 0xc040
-#define EPLL_CON0 0xc110
-#define EPLL_CON1 0xc114
-#define EPLL_CON2 0xc118
-#define G3D_PLL_CON0 0xc120
-#define G3D_PLL_CON1 0xc124
-#define G3D_PLL_CON2 0xc128
-#define ISP_PLL_CON0 0xc130
-#define ISP_PLL_CON1 0xc134
-#define ISP_PLL_CON2 0xc138
-#define DISP_PLL_CON0 0xc140
-#define DISP_PLL_CON1 0xc144
-#define DISP_PLL_CON2 0xc148
-#define SRC_TOP0 0xc210
-#define SRC_TOP1 0xc214
-#define SRC_CAM 0xc220
-#define SRC_TV 0xc224
-#define SRC_MFC 0xc228
-#define SRC_G3D 0xc22c
-#define SRC_LCD 0xc234
-#define SRC_ISP 0xc238
-#define SRC_MAUDIO 0xc23c
-#define SRC_FSYS 0xc240
-#define SRC_PERIL0 0xc250
-#define SRC_PERIL1 0xc254
-#define SRC_CAM1 0xc258
-#define SRC_TOP_ISP0 0xc25c
-#define SRC_TOP_ISP1 0xc260
-#define SRC_MASK_TOP 0xc310
-#define SRC_MASK_CAM 0xc320
-#define SRC_MASK_TV 0xc324
-#define SRC_MASK_LCD 0xc334
-#define SRC_MASK_ISP 0xc338
-#define SRC_MASK_MAUDIO 0xc33c
-#define SRC_MASK_FSYS 0xc340
-#define SRC_MASK_PERIL0 0xc350
-#define SRC_MASK_PERIL1 0xc354
-#define DIV_TOP 0xc510
-#define DIV_CAM 0xc520
-#define DIV_TV 0xc524
-#define DIV_MFC 0xc528
-#define DIV_G3D 0xc52c
-#define DIV_LCD 0xc534
-#define DIV_ISP 0xc538
-#define DIV_MAUDIO 0xc53c
-#define DIV_FSYS0 0xc540
-#define DIV_FSYS1 0xc544
-#define DIV_FSYS2 0xc548
-#define DIV_PERIL0 0xc550
-#define DIV_PERIL1 0xc554
-#define DIV_PERIL2 0xc558
-#define DIV_PERIL3 0xc55c
-#define DIV_PERIL4 0xc560
-#define DIV_PERIL5 0xc564
-#define DIV_CAM1 0xc568
-#define DIV_TOP_ISP1 0xc56c
-#define DIV_TOP_ISP0 0xc570
-#define CLKDIV2_RATIO 0xc580
-#define GATE_SCLK_CAM 0xc820
-#define GATE_SCLK_TV 0xc824
-#define GATE_SCLK_MFC 0xc828
-#define GATE_SCLK_G3D 0xc82c
-#define GATE_SCLK_LCD 0xc834
-#define GATE_SCLK_MAUDIO 0xc83c
-#define GATE_SCLK_FSYS 0xc840
-#define GATE_SCLK_PERIL 0xc850
-#define GATE_IP_CAM 0xc920
-#define GATE_IP_TV 0xc924
-#define GATE_IP_MFC 0xc928
-#define GATE_IP_G3D 0xc92c
-#define GATE_IP_LCD 0xc934
-#define GATE_IP_FSYS 0xc940
-#define GATE_IP_PERIL 0xc950
-#define GATE_BLOCK 0xc970
-#define APLL_LOCK 0x14000
-#define APLL_CON0 0x14100
-#define SRC_CPU 0x14200
-#define DIV_CPU0 0x14500
-#define DIV_CPU1 0x14504
-
-static const unsigned long exynos4415_cmu_clk_regs[] __initconst = {
- SRC_LEFTBUS,
- DIV_LEFTBUS,
- GATE_IP_LEFTBUS,
- GATE_IP_IMAGE,
- SRC_RIGHTBUS,
- DIV_RIGHTBUS,
- GATE_IP_RIGHTBUS,
- GATE_IP_PERIR,
- EPLL_LOCK,
- G3D_PLL_LOCK,
- DISP_PLL_LOCK,
- ISP_PLL_LOCK,
- EPLL_CON0,
- EPLL_CON1,
- EPLL_CON2,
- G3D_PLL_CON0,
- G3D_PLL_CON1,
- G3D_PLL_CON2,
- ISP_PLL_CON0,
- ISP_PLL_CON1,
- ISP_PLL_CON2,
- DISP_PLL_CON0,
- DISP_PLL_CON1,
- DISP_PLL_CON2,
- SRC_TOP0,
- SRC_TOP1,
- SRC_CAM,
- SRC_TV,
- SRC_MFC,
- SRC_G3D,
- SRC_LCD,
- SRC_ISP,
- SRC_MAUDIO,
- SRC_FSYS,
- SRC_PERIL0,
- SRC_PERIL1,
- SRC_CAM1,
- SRC_TOP_ISP0,
- SRC_TOP_ISP1,
- SRC_MASK_TOP,
- SRC_MASK_CAM,
- SRC_MASK_TV,
- SRC_MASK_LCD,
- SRC_MASK_ISP,
- SRC_MASK_MAUDIO,
- SRC_MASK_FSYS,
- SRC_MASK_PERIL0,
- SRC_MASK_PERIL1,
- DIV_TOP,
- DIV_CAM,
- DIV_TV,
- DIV_MFC,
- DIV_G3D,
- DIV_LCD,
- DIV_ISP,
- DIV_MAUDIO,
- DIV_FSYS0,
- DIV_FSYS1,
- DIV_FSYS2,
- DIV_PERIL0,
- DIV_PERIL1,
- DIV_PERIL2,
- DIV_PERIL3,
- DIV_PERIL4,
- DIV_PERIL5,
- DIV_CAM1,
- DIV_TOP_ISP1,
- DIV_TOP_ISP0,
- CLKDIV2_RATIO,
- GATE_SCLK_CAM,
- GATE_SCLK_TV,
- GATE_SCLK_MFC,
- GATE_SCLK_G3D,
- GATE_SCLK_LCD,
- GATE_SCLK_MAUDIO,
- GATE_SCLK_FSYS,
- GATE_SCLK_PERIL,
- GATE_IP_CAM,
- GATE_IP_TV,
- GATE_IP_MFC,
- GATE_IP_G3D,
- GATE_IP_LCD,
- GATE_IP_FSYS,
- GATE_IP_PERIL,
- GATE_BLOCK,
- APLL_LOCK,
- APLL_CON0,
- SRC_CPU,
- DIV_CPU0,
- DIV_CPU1,
-};
-
-/* list of all parent clock list */
-PNAME(mout_g3d_pllsrc_p) = { "fin_pll", };
-
-PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
-PNAME(mout_g3d_pll_p) = { "fin_pll", "fout_g3d_pll", };
-PNAME(mout_isp_pll_p) = { "fin_pll", "fout_isp_pll", };
-PNAME(mout_disp_pll_p) = { "fin_pll", "fout_disp_pll", };
-
-PNAME(mout_mpll_user_p) = { "fin_pll", "div_mpll_pre", };
-PNAME(mout_epll_p) = { "fin_pll", "fout_epll", };
-PNAME(mout_core_p) = { "mout_apll", "mout_mpll_user_c", };
-PNAME(mout_hpm_p) = { "mout_apll", "mout_mpll_user_c", };
-
-PNAME(mout_ebi_p) = { "div_aclk_200", "div_aclk_160", };
-PNAME(mout_ebi_1_p) = { "mout_ebi", "mout_g3d_pll", };
-
-PNAME(mout_gdl_p) = { "mout_mpll_user_l", };
-PNAME(mout_gdr_p) = { "mout_mpll_user_r", };
-
-PNAME(mout_aclk_266_p) = { "mout_mpll_user_t", "mout_g3d_pll", };
-
-PNAME(group_epll_g3dpll_p) = { "mout_epll", "mout_g3d_pll" };
-PNAME(group_sclk_p) = { "xxti", "xusbxti",
- "none", "mout_isp_pll",
- "none", "none", "div_mpll_pre",
- "mout_epll", "mout_g3d_pll", };
-PNAME(group_spdif_p) = { "mout_audio0", "mout_audio1",
- "mout_audio2", "spdif_extclk", };
-PNAME(group_sclk_audio2_p) = { "audiocdclk2", "none",
- "none", "mout_isp_pll",
- "mout_disp_pll", "xusbxti",
- "div_mpll_pre", "mout_epll",
- "mout_g3d_pll", };
-PNAME(group_sclk_audio1_p) = { "audiocdclk1", "none",
- "none", "mout_isp_pll",
- "mout_disp_pll", "xusbxti",
- "div_mpll_pre", "mout_epll",
- "mout_g3d_pll", };
-PNAME(group_sclk_audio0_p) = { "audiocdclk0", "none",
- "none", "mout_isp_pll",
- "mout_disp_pll", "xusbxti",
- "div_mpll_pre", "mout_epll",
- "mout_g3d_pll", };
-PNAME(group_fimc_lclk_p) = { "xxti", "xusbxti",
- "none", "mout_isp_pll",
- "none", "mout_disp_pll",
- "mout_mpll_user_t", "mout_epll",
- "mout_g3d_pll", };
-PNAME(group_sclk_fimd0_p) = { "xxti", "xusbxti",
- "m_bitclkhsdiv4_4l", "mout_isp_pll",
- "mout_disp_pll", "sclk_hdmiphy",
- "div_mpll_pre", "mout_epll",
- "mout_g3d_pll", };
-PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy" };
-PNAME(mout_mfc_p) = { "mout_mfc_0", "mout_mfc_1" };
-PNAME(mout_g3d_p) = { "mout_g3d_0", "mout_g3d_1" };
-PNAME(mout_jpeg_p) = { "mout_jpeg_0", "mout_jpeg_1" };
-PNAME(mout_jpeg1_p) = { "mout_epll", "mout_g3d_pll" };
-PNAME(group_aclk_isp0_300_p) = { "mout_isp_pll", "div_mpll_pre" };
-PNAME(group_aclk_isp0_400_user_p) = { "fin_pll", "div_aclk_400_mcuisp" };
-PNAME(group_aclk_isp0_300_user_p) = { "fin_pll", "mout_aclk_isp0_300" };
-PNAME(group_aclk_isp1_300_user_p) = { "fin_pll", "mout_aclk_isp1_300" };
-PNAME(group_mout_mpll_user_t_p) = { "mout_mpll_user_t" };
-
-static const struct samsung_fixed_factor_clock exynos4415_fixed_factor_clks[] __initconst = {
- /* HACK: fin_pll hardcoded to xusbxti until detection is implemented. */
- FFACTOR(CLK_FIN_PLL, "fin_pll", "xusbxti", 1, 1, 0),
-};
-
-static const struct samsung_fixed_rate_clock exynos4415_fixed_rate_clks[] __initconst = {
- FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, 0, 27000000),
-};
-
-static const struct samsung_mux_clock exynos4415_mux_clks[] __initconst = {
- /*
- * NOTE: Following table is sorted by register address in ascending
- * order and then bitfield shift in descending order, as it is done
- * in the User's Manual. When adding new entries, please make sure
- * that the order is preserved, to avoid merge conflicts and make
- * further work with defined data easier.
- */
-
- /* SRC_LEFTBUS */
- MUX(CLK_MOUT_MPLL_USER_L, "mout_mpll_user_l", mout_mpll_user_p,
- SRC_LEFTBUS, 4, 1),
- MUX(CLK_MOUT_GDL, "mout_gdl", mout_gdl_p, SRC_LEFTBUS, 0, 1),
-
- /* SRC_RIGHTBUS */
- MUX(CLK_MOUT_MPLL_USER_R, "mout_mpll_user_r", mout_mpll_user_p,
- SRC_RIGHTBUS, 4, 1),
- MUX(CLK_MOUT_GDR, "mout_gdr", mout_gdr_p, SRC_RIGHTBUS, 0, 1),
-
- /* SRC_TOP0 */
- MUX(CLK_MOUT_EBI, "mout_ebi", mout_ebi_p, SRC_TOP0, 28, 1),
- MUX(CLK_MOUT_ACLK_200, "mout_aclk_200", group_mout_mpll_user_t_p,
- SRC_TOP0, 24, 1),
- MUX(CLK_MOUT_ACLK_160, "mout_aclk_160", group_mout_mpll_user_t_p,
- SRC_TOP0, 20, 1),
- MUX(CLK_MOUT_ACLK_100, "mout_aclk_100", group_mout_mpll_user_t_p,
- SRC_TOP0, 16, 1),
- MUX(CLK_MOUT_ACLK_266, "mout_aclk_266", mout_aclk_266_p,
- SRC_TOP0, 12, 1),
- MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p,
- SRC_TOP0, 8, 1),
- MUX(CLK_MOUT_EPLL, "mout_epll", mout_epll_p, SRC_TOP0, 4, 1),
- MUX(CLK_MOUT_EBI_1, "mout_ebi_1", mout_ebi_1_p, SRC_TOP0, 0, 1),
-
- /* SRC_TOP1 */
- MUX(CLK_MOUT_ISP_PLL, "mout_isp_pll", mout_isp_pll_p,
- SRC_TOP1, 28, 1),
- MUX(CLK_MOUT_DISP_PLL, "mout_disp_pll", mout_disp_pll_p,
- SRC_TOP1, 16, 1),
- MUX(CLK_MOUT_MPLL_USER_T, "mout_mpll_user_t", mout_mpll_user_p,
- SRC_TOP1, 12, 1),
- MUX(CLK_MOUT_ACLK_400_MCUISP, "mout_aclk_400_mcuisp",
- group_mout_mpll_user_t_p, SRC_TOP1, 8, 1),
- MUX(CLK_MOUT_G3D_PLLSRC, "mout_g3d_pllsrc", mout_g3d_pllsrc_p,
- SRC_TOP1, 0, 1),
-
- /* SRC_CAM */
- MUX(CLK_MOUT_CSIS1, "mout_csis1", group_fimc_lclk_p, SRC_CAM, 28, 4),
- MUX(CLK_MOUT_CSIS0, "mout_csis0", group_fimc_lclk_p, SRC_CAM, 24, 4),
- MUX(CLK_MOUT_CAM1, "mout_cam1", group_fimc_lclk_p, SRC_CAM, 20, 4),
- MUX(CLK_MOUT_FIMC3_LCLK, "mout_fimc3_lclk", group_fimc_lclk_p, SRC_CAM,
- 12, 4),
- MUX(CLK_MOUT_FIMC2_LCLK, "mout_fimc2_lclk", group_fimc_lclk_p, SRC_CAM,
- 8, 4),
- MUX(CLK_MOUT_FIMC1_LCLK, "mout_fimc1_lclk", group_fimc_lclk_p, SRC_CAM,
- 4, 4),
- MUX(CLK_MOUT_FIMC0_LCLK, "mout_fimc0_lclk", group_fimc_lclk_p, SRC_CAM,
- 0, 4),
-
- /* SRC_TV */
- MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1),
-
- /* SRC_MFC */
- MUX(CLK_MOUT_MFC, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1),
- MUX(CLK_MOUT_MFC_1, "mout_mfc_1", group_epll_g3dpll_p, SRC_MFC, 4, 1),
- MUX(CLK_MOUT_MFC_0, "mout_mfc_0", group_mout_mpll_user_t_p, SRC_MFC, 0,
- 1),
-
- /* SRC_G3D */
- MUX(CLK_MOUT_G3D, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1),
- MUX(CLK_MOUT_G3D_1, "mout_g3d_1", group_epll_g3dpll_p, SRC_G3D, 4, 1),
- MUX(CLK_MOUT_G3D_0, "mout_g3d_0", group_mout_mpll_user_t_p, SRC_G3D, 0,
- 1),
-
- /* SRC_LCD */
- MUX(CLK_MOUT_MIPI0, "mout_mipi0", group_fimc_lclk_p, SRC_LCD, 12, 4),
- MUX(CLK_MOUT_FIMD0, "mout_fimd0", group_sclk_fimd0_p, SRC_LCD, 0, 4),
-
- /* SRC_ISP */
- MUX(CLK_MOUT_TSADC_ISP, "mout_tsadc_isp", group_fimc_lclk_p, SRC_ISP,
- 16, 4),
- MUX(CLK_MOUT_UART_ISP, "mout_uart_isp", group_fimc_lclk_p, SRC_ISP,
- 12, 4),
- MUX(CLK_MOUT_SPI1_ISP, "mout_spi1_isp", group_fimc_lclk_p, SRC_ISP,
- 8, 4),
- MUX(CLK_MOUT_SPI0_ISP, "mout_spi0_isp", group_fimc_lclk_p, SRC_ISP,
- 4, 4),
- MUX(CLK_MOUT_PWM_ISP, "mout_pwm_isp", group_fimc_lclk_p, SRC_ISP,
- 0, 4),
-
- /* SRC_MAUDIO */
- MUX(CLK_MOUT_AUDIO0, "mout_audio0", group_sclk_audio0_p, SRC_MAUDIO,
- 0, 4),
-
- /* SRC_FSYS */
- MUX(CLK_MOUT_TSADC, "mout_tsadc", group_sclk_p, SRC_FSYS, 28, 4),
- MUX(CLK_MOUT_MMC2, "mout_mmc2", group_sclk_p, SRC_FSYS, 8, 4),
- MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 4),
- MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 4),
-
- /* SRC_PERIL0 */
- MUX(CLK_MOUT_UART3, "mout_uart3", group_sclk_p, SRC_PERIL0, 12, 4),
- MUX(CLK_MOUT_UART2, "mout_uart2", group_sclk_p, SRC_PERIL0, 8, 4),
- MUX(CLK_MOUT_UART1, "mout_uart1", group_sclk_p, SRC_PERIL0, 4, 4),
- MUX(CLK_MOUT_UART0, "mout_uart0", group_sclk_p, SRC_PERIL0, 0, 4),
-
- /* SRC_PERIL1 */
- MUX(CLK_MOUT_SPI2, "mout_spi2", group_sclk_p, SRC_PERIL1, 24, 4),
- MUX(CLK_MOUT_SPI1, "mout_spi1", group_sclk_p, SRC_PERIL1, 20, 4),
- MUX(CLK_MOUT_SPI0, "mout_spi0", group_sclk_p, SRC_PERIL1, 16, 4),
- MUX(CLK_MOUT_SPDIF, "mout_spdif", group_spdif_p, SRC_PERIL1, 8, 4),
- MUX(CLK_MOUT_AUDIO2, "mout_audio2", group_sclk_audio2_p, SRC_PERIL1,
- 4, 4),
- MUX(CLK_MOUT_AUDIO1, "mout_audio1", group_sclk_audio1_p, SRC_PERIL1,
- 0, 4),
-
- /* SRC_CPU */
- MUX(CLK_MOUT_MPLL_USER_C, "mout_mpll_user_c", mout_mpll_user_p,
- SRC_CPU, 24, 1),
- MUX(CLK_MOUT_HPM, "mout_hpm", mout_hpm_p, SRC_CPU, 20, 1),
- MUX_F(CLK_MOUT_CORE, "mout_core", mout_core_p, SRC_CPU, 16, 1, 0,
- CLK_MUX_READ_ONLY),
- MUX_F(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
- CLK_SET_RATE_PARENT, 0),
-
- /* SRC_CAM1 */
- MUX(CLK_MOUT_PXLASYNC_CSIS1_FIMC, "mout_pxlasync_csis1",
- group_fimc_lclk_p, SRC_CAM1, 20, 1),
- MUX(CLK_MOUT_PXLASYNC_CSIS0_FIMC, "mout_pxlasync_csis0",
- group_fimc_lclk_p, SRC_CAM1, 16, 1),
- MUX(CLK_MOUT_JPEG, "mout_jpeg", mout_jpeg_p, SRC_CAM1, 8, 1),
- MUX(CLK_MOUT_JPEG1, "mout_jpeg_1", mout_jpeg1_p, SRC_CAM1, 4, 1),
- MUX(CLK_MOUT_JPEG0, "mout_jpeg_0", group_mout_mpll_user_t_p, SRC_CAM1,
- 0, 1),
-
- /* SRC_TOP_ISP0 */
- MUX(CLK_MOUT_ACLK_ISP0_300, "mout_aclk_isp0_300",
- group_aclk_isp0_300_p, SRC_TOP_ISP0, 8, 1),
- MUX(CLK_MOUT_ACLK_ISP0_400, "mout_aclk_isp0_400_user",
- group_aclk_isp0_400_user_p, SRC_TOP_ISP0, 4, 1),
- MUX(CLK_MOUT_ACLK_ISP0_300_USER, "mout_aclk_isp0_300_user",
- group_aclk_isp0_300_user_p, SRC_TOP_ISP0, 0, 1),
-
- /* SRC_TOP_ISP1 */
- MUX(CLK_MOUT_ACLK_ISP1_300, "mout_aclk_isp1_300",
- group_aclk_isp0_300_p, SRC_TOP_ISP1, 4, 1),
- MUX(CLK_MOUT_ACLK_ISP1_300_USER, "mout_aclk_isp1_300_user",
- group_aclk_isp1_300_user_p, SRC_TOP_ISP1, 0, 1),
-};
-
-static const struct samsung_div_clock exynos4415_div_clks[] __initconst = {
- /*
- * NOTE: Following table is sorted by register address in ascending
- * order and then bitfield shift in descending order, as it is done
- * in the User's Manual. When adding new entries, please make sure
- * that the order is preserved, to avoid merge conflicts and make
- * further work with defined data easier.
- */
-
- /* DIV_LEFTBUS */
- DIV(CLK_DIV_GPL, "div_gpl", "div_gdl", DIV_LEFTBUS, 4, 3),
- DIV(CLK_DIV_GDL, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 4),
-
- /* DIV_RIGHTBUS */
- DIV(CLK_DIV_GPR, "div_gpr", "div_gdr", DIV_RIGHTBUS, 4, 3),
- DIV(CLK_DIV_GDR, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 4),
-
- /* DIV_TOP */
- DIV(CLK_DIV_ACLK_400_MCUISP, "div_aclk_400_mcuisp",
- "mout_aclk_400_mcuisp", DIV_TOP, 24, 3),
- DIV(CLK_DIV_EBI, "div_ebi", "mout_ebi_1", DIV_TOP, 16, 3),
- DIV(CLK_DIV_ACLK_200, "div_aclk_200", "mout_aclk_200", DIV_TOP, 12, 3),
- DIV(CLK_DIV_ACLK_160, "div_aclk_160", "mout_aclk_160", DIV_TOP, 8, 3),
- DIV(CLK_DIV_ACLK_100, "div_aclk_100", "mout_aclk_100", DIV_TOP, 4, 4),
- DIV(CLK_DIV_ACLK_266, "div_aclk_266", "mout_aclk_266", DIV_TOP, 0, 3),
-
- /* DIV_CAM */
- DIV(CLK_DIV_CSIS1, "div_csis1", "mout_csis1", DIV_CAM, 28, 4),
- DIV(CLK_DIV_CSIS0, "div_csis0", "mout_csis0", DIV_CAM, 24, 4),
- DIV(CLK_DIV_CAM1, "div_cam1", "mout_cam1", DIV_CAM, 20, 4),
- DIV(CLK_DIV_FIMC3_LCLK, "div_fimc3_lclk", "mout_fimc3_lclk", DIV_CAM,
- 12, 4),
- DIV(CLK_DIV_FIMC2_LCLK, "div_fimc2_lclk", "mout_fimc2_lclk", DIV_CAM,
- 8, 4),
- DIV(CLK_DIV_FIMC1_LCLK, "div_fimc1_lclk", "mout_fimc1_lclk", DIV_CAM,
- 4, 4),
- DIV(CLK_DIV_FIMC0_LCLK, "div_fimc0_lclk", "mout_fimc0_lclk", DIV_CAM,
- 0, 4),
-
- /* DIV_TV */
- DIV(CLK_DIV_TV_BLK, "div_tv_blk", "mout_g3d_pll", DIV_TV, 0, 4),
-
- /* DIV_MFC */
- DIV(CLK_DIV_MFC, "div_mfc", "mout_mfc", DIV_MFC, 0, 4),
-
- /* DIV_G3D */
- DIV(CLK_DIV_G3D, "div_g3d", "mout_g3d", DIV_G3D, 0, 4),
-
- /* DIV_LCD */
- DIV_F(CLK_DIV_MIPI0_PRE, "div_mipi0_pre", "div_mipi0", DIV_LCD, 20, 4,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_MIPI0, "div_mipi0", "mout_mipi0", DIV_LCD, 16, 4),
- DIV(CLK_DIV_FIMD0, "div_fimd0", "mout_fimd0", DIV_LCD, 0, 4),
-
- /* DIV_ISP */
- DIV(CLK_DIV_UART_ISP, "div_uart_isp", "mout_uart_isp", DIV_ISP, 28, 4),
- DIV_F(CLK_DIV_SPI1_ISP_PRE, "div_spi1_isp_pre", "div_spi1_isp",
- DIV_ISP, 20, 8, CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_SPI1_ISP, "div_spi1_isp", "mout_spi1_isp", DIV_ISP, 16, 4),
- DIV_F(CLK_DIV_SPI0_ISP_PRE, "div_spi0_isp_pre", "div_spi0_isp",
- DIV_ISP, 8, 8, CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 4, 4),
- DIV(CLK_DIV_PWM_ISP, "div_pwm_isp", "mout_pwm_isp", DIV_ISP, 0, 4),
-
- /* DIV_MAUDIO */
- DIV(CLK_DIV_PCM0, "div_pcm0", "div_audio0", DIV_MAUDIO, 4, 8),
- DIV(CLK_DIV_AUDIO0, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4),
-
- /* DIV_FSYS0 */
- DIV_F(CLK_DIV_TSADC_PRE, "div_tsadc_pre", "div_tsadc", DIV_FSYS0, 8, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_TSADC, "div_tsadc", "mout_tsadc", DIV_FSYS0, 0, 4),
-
- /* DIV_FSYS1 */
- DIV_F(CLK_DIV_MMC1_PRE, "div_mmc1_pre", "div_mmc1", DIV_FSYS1, 24, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_MMC1, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
- DIV_F(CLK_DIV_MMC0_PRE, "div_mmc0_pre", "div_mmc0", DIV_FSYS1, 8, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_MMC0, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
-
- /* DIV_FSYS2 */
- DIV_F(CLK_DIV_MMC2_PRE, "div_mmc2_pre", "div_mmc2", DIV_FSYS2, 8, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV_F(CLK_DIV_MMC2_PRE, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4,
- CLK_SET_RATE_PARENT, 0),
-
- /* DIV_PERIL0 */
- DIV(CLK_DIV_UART3, "div_uart3", "mout_uart3", DIV_PERIL0, 12, 4),
- DIV(CLK_DIV_UART2, "div_uart2", "mout_uart2", DIV_PERIL0, 8, 4),
- DIV(CLK_DIV_UART1, "div_uart1", "mout_uart1", DIV_PERIL0, 4, 4),
- DIV(CLK_DIV_UART0, "div_uart0", "mout_uart0", DIV_PERIL0, 0, 4),
-
- /* DIV_PERIL1 */
- DIV_F(CLK_DIV_SPI1_PRE, "div_spi1_pre", "div_spi1", DIV_PERIL1, 24, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_SPI1, "div_spi1", "mout_spi1", DIV_PERIL1, 16, 4),
- DIV_F(CLK_DIV_SPI0_PRE, "div_spi0_pre", "div_spi0", DIV_PERIL1, 8, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_SPI0, "div_spi0", "mout_spi0", DIV_PERIL1, 0, 4),
-
- /* DIV_PERIL2 */
- DIV_F(CLK_DIV_SPI2_PRE, "div_spi2_pre", "div_spi2", DIV_PERIL2, 8, 8,
- CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_SPI2, "div_spi2", "mout_spi2", DIV_PERIL2, 0, 4),
-
- /* DIV_PERIL4 */
- DIV(CLK_DIV_PCM2, "div_pcm2", "div_audio2", DIV_PERIL4, 20, 8),
- DIV(CLK_DIV_AUDIO2, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4),
- DIV(CLK_DIV_PCM1, "div_pcm1", "div_audio1", DIV_PERIL4, 20, 8),
- DIV(CLK_DIV_AUDIO1, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4),
-
- /* DIV_PERIL5 */
- DIV(CLK_DIV_I2S1, "div_i2s1", "div_audio1", DIV_PERIL5, 0, 6),
-
- /* DIV_CAM1 */
- DIV(CLK_DIV_PXLASYNC_CSIS1_FIMC, "div_pxlasync_csis1_fimc",
- "mout_pxlasync_csis1", DIV_CAM1, 24, 4),
- DIV(CLK_DIV_PXLASYNC_CSIS0_FIMC, "div_pxlasync_csis0_fimc",
- "mout_pxlasync_csis0", DIV_CAM1, 20, 4),
- DIV(CLK_DIV_JPEG, "div_jpeg", "mout_jpeg", DIV_CAM1, 0, 4),
-
- /* DIV_CPU0 */
- DIV(CLK_DIV_CORE2, "div_core2", "div_core", DIV_CPU0, 28, 3),
- DIV_F(CLK_DIV_APLL, "div_apll", "mout_apll", DIV_CPU0, 24, 3,
- CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY),
- DIV(CLK_DIV_PCLK_DBG, "div_pclk_dbg", "div_core2", DIV_CPU0, 20, 3),
- DIV(CLK_DIV_ATB, "div_atb", "div_core2", DIV_CPU0, 16, 3),
- DIV(CLK_DIV_PERIPH, "div_periph", "div_core2", DIV_CPU0, 12, 3),
- DIV(CLK_DIV_COREM1, "div_corem1", "div_core2", DIV_CPU0, 8, 3),
- DIV(CLK_DIV_COREM0, "div_corem0", "div_core2", DIV_CPU0, 4, 3),
- DIV_F(CLK_DIV_CORE, "div_core", "mout_core", DIV_CPU0, 0, 3,
- CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY),
-
- /* DIV_CPU1 */
- DIV(CLK_DIV_HPM, "div_hpm", "div_copy", DIV_CPU1, 4, 3),
- DIV(CLK_DIV_COPY, "div_copy", "mout_hpm", DIV_CPU1, 0, 3),
-};
-
-static const struct samsung_gate_clock exynos4415_gate_clks[] __initconst = {
- /*
- * NOTE: Following table is sorted by register address in ascending
- * order and then bitfield shift in descending order, as it is done
- * in the User's Manual. When adding new entries, please make sure
- * that the order is preserved, to avoid merge conflicts and make
- * further work with defined data easier.
- */
-
- /* GATE_IP_LEFTBUS */
- GATE(CLK_ASYNC_G3D, "async_g3d", "div_aclk_100", GATE_IP_LEFTBUS, 6,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_MFCL, "async_mfcl", "div_aclk_100", GATE_IP_LEFTBUS, 4,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_TVX, "async_tvx", "div_aclk_100", GATE_IP_LEFTBUS, 3,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PPMULEFT, "ppmuleft", "div_aclk_100", GATE_IP_LEFTBUS, 1,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_GPIO_LEFT, "gpio_left", "div_aclk_100", GATE_IP_LEFTBUS, 0,
- CLK_IGNORE_UNUSED, 0),
-
- /* GATE_IP_IMAGE */
- GATE(CLK_PPMUIMAGE, "ppmuimage", "div_aclk_100", GATE_IP_IMAGE,
- 9, 0, 0),
- GATE(CLK_QEMDMA2, "qe_mdma2", "div_aclk_100", GATE_IP_IMAGE,
- 8, 0, 0),
- GATE(CLK_QEROTATOR, "qe_rotator", "div_aclk_100", GATE_IP_IMAGE,
- 7, 0, 0),
- GATE(CLK_SMMUMDMA2, "smmu_mdam2", "div_aclk_100", GATE_IP_IMAGE,
- 5, 0, 0),
- GATE(CLK_SMMUROTATOR, "smmu_rotator", "div_aclk_100", GATE_IP_IMAGE,
- 4, 0, 0),
- GATE(CLK_MDMA2, "mdma2", "div_aclk_100", GATE_IP_IMAGE, 2, 0, 0),
- GATE(CLK_ROTATOR, "rotator", "div_aclk_100", GATE_IP_IMAGE, 1, 0, 0),
-
- /* GATE_IP_RIGHTBUS */
- GATE(CLK_ASYNC_ISPMX, "async_ispmx", "div_aclk_100",
- GATE_IP_RIGHTBUS, 9, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_MAUDIOX, "async_maudiox", "div_aclk_100",
- GATE_IP_RIGHTBUS, 7, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_MFCR, "async_mfcr", "div_aclk_100",
- GATE_IP_RIGHTBUS, 6, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_FSYSD, "async_fsysd", "div_aclk_100",
- GATE_IP_RIGHTBUS, 5, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_LCD0X, "async_lcd0x", "div_aclk_100",
- GATE_IP_RIGHTBUS, 3, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_ASYNC_CAMX, "async_camx", "div_aclk_100",
- GATE_IP_RIGHTBUS, 2, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PPMURIGHT, "ppmuright", "div_aclk_100",
- GATE_IP_RIGHTBUS, 1, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_GPIO_RIGHT, "gpio_right", "div_aclk_100",
- GATE_IP_RIGHTBUS, 0, CLK_IGNORE_UNUSED, 0),
-
- /* GATE_IP_PERIR */
- GATE(CLK_ANTIRBK_APBIF, "antirbk_apbif", "div_aclk_100",
- GATE_IP_PERIR, 24, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_EFUSE_WRITER_APBIF, "efuse_writer_apbif", "div_aclk_100",
- GATE_IP_PERIR, 23, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_MONOCNT, "monocnt", "div_aclk_100", GATE_IP_PERIR, 22,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC6, "tzpc6", "div_aclk_100", GATE_IP_PERIR, 21,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PROVISIONKEY1, "provisionkey1", "div_aclk_100",
- GATE_IP_PERIR, 20, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PROVISIONKEY0, "provisionkey0", "div_aclk_100",
- GATE_IP_PERIR, 19, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_CMU_ISPPART, "cmu_isppart", "div_aclk_100", GATE_IP_PERIR, 18,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TMU_APBIF, "tmu_apbif", "div_aclk_100",
- GATE_IP_PERIR, 17, 0, 0),
- GATE(CLK_KEYIF, "keyif", "div_aclk_100", GATE_IP_PERIR, 16, 0, 0),
- GATE(CLK_RTC, "rtc", "div_aclk_100", GATE_IP_PERIR, 15, 0, 0),
- GATE(CLK_WDT, "wdt", "div_aclk_100", GATE_IP_PERIR, 14, 0, 0),
- GATE(CLK_MCT, "mct", "div_aclk_100", GATE_IP_PERIR, 13, 0, 0),
- GATE(CLK_SECKEY, "seckey", "div_aclk_100", GATE_IP_PERIR, 12,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_HDMI_CEC, "hdmi_cec", "div_aclk_100", GATE_IP_PERIR, 11,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC5, "tzpc5", "div_aclk_100", GATE_IP_PERIR, 10,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC4, "tzpc4", "div_aclk_100", GATE_IP_PERIR, 9,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC3, "tzpc3", "div_aclk_100", GATE_IP_PERIR, 8,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC2, "tzpc2", "div_aclk_100", GATE_IP_PERIR, 7,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC1, "tzpc1", "div_aclk_100", GATE_IP_PERIR, 6,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_TZPC0, "tzpc0", "div_aclk_100", GATE_IP_PERIR, 5,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_CMU_COREPART, "cmu_corepart", "div_aclk_100", GATE_IP_PERIR, 4,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_CMU_TOPPART, "cmu_toppart", "div_aclk_100", GATE_IP_PERIR, 3,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PMU_APBIF, "pmu_apbif", "div_aclk_100", GATE_IP_PERIR, 2,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_SYSREG, "sysreg", "div_aclk_100", GATE_IP_PERIR, 1,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_CHIP_ID, "chip_id", "div_aclk_100", GATE_IP_PERIR, 0,
- CLK_IGNORE_UNUSED, 0),
-
- /* GATE_SCLK_CAM - non-completed */
- GATE(CLK_SCLK_PXLAYSNC_CSIS1_FIMC, "sclk_pxlasync_csis1_fimc",
- "div_pxlasync_csis1_fimc", GATE_SCLK_CAM, 11,
- CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_PXLAYSNC_CSIS0_FIMC, "sclk_pxlasync_csis0_fimc",
- "div_pxlasync_csis0_fimc", GATE_SCLK_CAM,
- 10, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_JPEG, "sclk_jpeg", "div_jpeg",
- GATE_SCLK_CAM, 8, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_CSIS1, "sclk_csis1", "div_csis1",
- GATE_SCLK_CAM, 7, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_CSIS0, "sclk_csis0", "div_csis0",
- GATE_SCLK_CAM, 6, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_CAM1, "sclk_cam1", "div_cam1",
- GATE_SCLK_CAM, 5, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_FIMC3_LCLK, "sclk_fimc3_lclk", "div_fimc3_lclk",
- GATE_SCLK_CAM, 3, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_FIMC2_LCLK, "sclk_fimc2_lclk", "div_fimc2_lclk",
- GATE_SCLK_CAM, 2, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_FIMC1_LCLK, "sclk_fimc1_lclk", "div_fimc1_lclk",
- GATE_SCLK_CAM, 1, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_FIMC0_LCLK, "sclk_fimc0_lclk", "div_fimc0_lclk",
- GATE_SCLK_CAM, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_TV */
- GATE(CLK_SCLK_PIXEL, "sclk_pixel", "div_tv_blk",
- GATE_SCLK_TV, 3, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi",
- GATE_SCLK_TV, 2, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_MIXER, "sclk_mixer", "div_tv_blk",
- GATE_SCLK_TV, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_MFC */
- GATE(CLK_SCLK_MFC, "sclk_mfc", "div_mfc",
- GATE_SCLK_MFC, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_G3D */
- GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d",
- GATE_SCLK_G3D, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_LCD */
- GATE(CLK_SCLK_MIPIDPHY4L, "sclk_mipidphy4l", "div_mipi0",
- GATE_SCLK_LCD, 4, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_MIPI0, "sclk_mipi0", "div_mipi0_pre",
- GATE_SCLK_LCD, 3, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_MDNIE0, "sclk_mdnie0", "div_fimd0",
- GATE_SCLK_LCD, 1, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_FIMD0, "sclk_fimd0", "div_fimd0",
- GATE_SCLK_LCD, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_MAUDIO */
- GATE(CLK_SCLK_PCM0, "sclk_pcm0", "div_pcm0",
- GATE_SCLK_MAUDIO, 1, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_AUDIO0, "sclk_audio0", "div_audio0",
- GATE_SCLK_MAUDIO, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_FSYS */
- GATE(CLK_SCLK_TSADC, "sclk_tsadc", "div_tsadc_pre",
- GATE_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_EBI, "sclk_ebi", "div_ebi",
- GATE_SCLK_FSYS, 6, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_MMC2, "sclk_mmc2", "div_mmc2_pre",
- GATE_SCLK_FSYS, 2, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_MMC1, "sclk_mmc1", "div_mmc1_pre",
- GATE_SCLK_FSYS, 1, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_MMC0, "sclk_mmc0", "div_mmc0_pre",
- GATE_SCLK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_SCLK_PERIL */
- GATE(CLK_SCLK_I2S, "sclk_i2s1", "div_i2s1",
- GATE_SCLK_PERIL, 18, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_PCM2, "sclk_pcm2", "div_pcm2",
- GATE_SCLK_PERIL, 16, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_PCM1, "sclk_pcm1", "div_pcm1",
- GATE_SCLK_PERIL, 15, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_AUDIO2, "sclk_audio2", "div_audio2",
- GATE_SCLK_PERIL, 14, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_AUDIO1, "sclk_audio1", "div_audio1",
- GATE_SCLK_PERIL, 13, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif",
- GATE_SCLK_PERIL, 10, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_SPI2, "sclk_spi2", "div_spi2_pre",
- GATE_SCLK_PERIL, 8, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_SPI1, "sclk_spi1", "div_spi1_pre",
- GATE_SCLK_PERIL, 7, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_SPI0, "sclk_spi0", "div_spi0_pre",
- GATE_SCLK_PERIL, 6, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_UART3, "sclk_uart3", "div_uart3",
- GATE_SCLK_PERIL, 3, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_UART2, "sclk_uart2", "div_uart2",
- GATE_SCLK_PERIL, 2, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_UART1, "sclk_uart1", "div_uart1",
- GATE_SCLK_PERIL, 1, CLK_SET_RATE_PARENT, 0),
- GATE(CLK_SCLK_UART0, "sclk_uart0", "div_uart0",
- GATE_SCLK_PERIL, 0, CLK_SET_RATE_PARENT, 0),
-
- /* GATE_IP_CAM */
- GATE(CLK_SMMUFIMC_LITE2, "smmufimc_lite2", "div_aclk_160", GATE_IP_CAM,
- 22, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_FIMC_LITE2, "fimc_lite2", "div_aclk_160", GATE_IP_CAM,
- 20, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PIXELASYNCM1, "pixelasyncm1", "div_aclk_160", GATE_IP_CAM,
- 18, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PIXELASYNCM0, "pixelasyncm0", "div_aclk_160", GATE_IP_CAM,
- 17, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PPMUCAMIF, "ppmucamif", "div_aclk_160", GATE_IP_CAM,
- 16, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_SMMUJPEG, "smmujpeg", "div_aclk_160", GATE_IP_CAM, 11, 0, 0),
- GATE(CLK_SMMUFIMC3, "smmufimc3", "div_aclk_160", GATE_IP_CAM, 10, 0, 0),
- GATE(CLK_SMMUFIMC2, "smmufimc2", "div_aclk_160", GATE_IP_CAM, 9, 0, 0),
- GATE(CLK_SMMUFIMC1, "smmufimc1", "div_aclk_160", GATE_IP_CAM, 8, 0, 0),
- GATE(CLK_SMMUFIMC0, "smmufimc0", "div_aclk_160", GATE_IP_CAM, 7, 0, 0),
- GATE(CLK_JPEG, "jpeg", "div_aclk_160", GATE_IP_CAM, 6, 0, 0),
- GATE(CLK_CSIS1, "csis1", "div_aclk_160", GATE_IP_CAM, 5, 0, 0),
- GATE(CLK_CSIS0, "csis0", "div_aclk_160", GATE_IP_CAM, 4, 0, 0),
- GATE(CLK_FIMC3, "fimc3", "div_aclk_160", GATE_IP_CAM, 3, 0, 0),
- GATE(CLK_FIMC2, "fimc2", "div_aclk_160", GATE_IP_CAM, 2, 0, 0),
- GATE(CLK_FIMC1, "fimc1", "div_aclk_160", GATE_IP_CAM, 1, 0, 0),
- GATE(CLK_FIMC0, "fimc0", "div_aclk_160", GATE_IP_CAM, 0, 0, 0),
-
- /* GATE_IP_TV */
- GATE(CLK_PPMUTV, "ppmutv", "div_aclk_100", GATE_IP_TV, 5, 0, 0),
- GATE(CLK_SMMUTV, "smmutv", "div_aclk_100", GATE_IP_TV, 4, 0, 0),
- GATE(CLK_HDMI, "hdmi", "div_aclk_100", GATE_IP_TV, 3, 0, 0),
- GATE(CLK_MIXER, "mixer", "div_aclk_100", GATE_IP_TV, 1, 0, 0),
- GATE(CLK_VP, "vp", "div_aclk_100", GATE_IP_TV, 0, 0, 0),
-
- /* GATE_IP_MFC */
- GATE(CLK_PPMUMFC_R, "ppmumfc_r", "div_aclk_200", GATE_IP_MFC, 4,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_PPMUMFC_L, "ppmumfc_l", "div_aclk_200", GATE_IP_MFC, 3,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_SMMUMFC_R, "smmumfc_r", "div_aclk_200", GATE_IP_MFC, 2, 0, 0),
- GATE(CLK_SMMUMFC_L, "smmumfc_l", "div_aclk_200", GATE_IP_MFC, 1, 0, 0),
- GATE(CLK_MFC, "mfc", "div_aclk_200", GATE_IP_MFC, 0, 0, 0),
-
- /* GATE_IP_G3D */
- GATE(CLK_PPMUG3D, "ppmug3d", "div_aclk_200", GATE_IP_G3D, 1,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_G3D, "g3d", "div_aclk_200", GATE_IP_G3D, 0, 0, 0),
-
- /* GATE_IP_LCD */
- GATE(CLK_PPMULCD0, "ppmulcd0", "div_aclk_160", GATE_IP_LCD, 5,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_SMMUFIMD0, "smmufimd0", "div_aclk_160", GATE_IP_LCD, 4, 0, 0),
- GATE(CLK_DSIM0, "dsim0", "div_aclk_160", GATE_IP_LCD, 3, 0, 0),
- GATE(CLK_SMIES, "smies", "div_aclk_160", GATE_IP_LCD, 2, 0, 0),
- GATE(CLK_MIE0, "mie0", "div_aclk_160", GATE_IP_LCD, 1, 0, 0),
- GATE(CLK_FIMD0, "fimd0", "div_aclk_160", GATE_IP_LCD, 0, 0, 0),
-
- /* GATE_IP_FSYS */
- GATE(CLK_TSADC, "tsadc", "div_aclk_200", GATE_IP_FSYS, 20, 0, 0),
- GATE(CLK_PPMUFILE, "ppmufile", "div_aclk_200", GATE_IP_FSYS, 17,
- CLK_IGNORE_UNUSED, 0),
- GATE(CLK_NFCON, "nfcon", "div_aclk_200", GATE_IP_FSYS, 16, 0, 0),
- GATE(CLK_USBDEVICE, "usbdevice", "div_aclk_200", GATE_IP_FSYS, 13,
- 0, 0),
- GATE(CLK_USBHOST, "usbhost", "div_aclk_200", GATE_IP_FSYS, 12, 0, 0),
- GATE(CLK_SROMC, "sromc", "div_aclk_200", GATE_IP_FSYS, 11, 0, 0),
- GATE(CLK_SDMMC2, "sdmmc2", "div_aclk_200", GATE_IP_FSYS, 7, 0, 0),
- GATE(CLK_SDMMC1, "sdmmc1", "div_aclk_200", GATE_IP_FSYS, 6, 0, 0),
- GATE(CLK_SDMMC0, "sdmmc0", "div_aclk_200", GATE_IP_FSYS, 5, 0, 0),
- GATE(CLK_PDMA1, "pdma1", "div_aclk_200", GATE_IP_FSYS, 1, 0, 0),
- GATE(CLK_PDMA0, "pdma0", "div_aclk_200", GATE_IP_FSYS, 0, 0, 0),
-
- /* GATE_IP_PERIL */
- GATE(CLK_SPDIF, "spdif", "div_aclk_100", GATE_IP_PERIL, 26, 0, 0),
- GATE(CLK_PWM, "pwm", "div_aclk_100", GATE_IP_PERIL, 24, 0, 0),
- GATE(CLK_PCM2, "pcm2", "div_aclk_100", GATE_IP_PERIL, 23, 0, 0),
- GATE(CLK_PCM1, "pcm1", "div_aclk_100", GATE_IP_PERIL, 22, 0, 0),
- GATE(CLK_I2S1, "i2s1", "div_aclk_100", GATE_IP_PERIL, 20, 0, 0),
- GATE(CLK_SPI2, "spi2", "div_aclk_100", GATE_IP_PERIL, 18, 0, 0),
- GATE(CLK_SPI1, "spi1", "div_aclk_100", GATE_IP_PERIL, 17, 0, 0),
- GATE(CLK_SPI0, "spi0", "div_aclk_100", GATE_IP_PERIL, 16, 0, 0),
- GATE(CLK_I2CHDMI, "i2chdmi", "div_aclk_100", GATE_IP_PERIL, 14, 0, 0),
- GATE(CLK_I2C7, "i2c7", "div_aclk_100", GATE_IP_PERIL, 13, 0, 0),
- GATE(CLK_I2C6, "i2c6", "div_aclk_100", GATE_IP_PERIL, 12, 0, 0),
- GATE(CLK_I2C5, "i2c5", "div_aclk_100", GATE_IP_PERIL, 11, 0, 0),
- GATE(CLK_I2C4, "i2c4", "div_aclk_100", GATE_IP_PERIL, 10, 0, 0),
- GATE(CLK_I2C3, "i2c3", "div_aclk_100", GATE_IP_PERIL, 9, 0, 0),
- GATE(CLK_I2C2, "i2c2", "div_aclk_100", GATE_IP_PERIL, 8, 0, 0),
- GATE(CLK_I2C1, "i2c1", "div_aclk_100", GATE_IP_PERIL, 7, 0, 0),
- GATE(CLK_I2C0, "i2c0", "div_aclk_100", GATE_IP_PERIL, 6, 0, 0),
- GATE(CLK_UART3, "uart3", "div_aclk_100", GATE_IP_PERIL, 3, 0, 0),
- GATE(CLK_UART2, "uart2", "div_aclk_100", GATE_IP_PERIL, 2, 0, 0),
- GATE(CLK_UART1, "uart1", "div_aclk_100", GATE_IP_PERIL, 1, 0, 0),
- GATE(CLK_UART0, "uart0", "div_aclk_100", GATE_IP_PERIL, 0, 0, 0),
-};
-
-/*
- * APLL & MPLL & BPLL & ISP_PLL & DISP_PLL & G3D_PLL
- */
-static const struct samsung_pll_rate_table exynos4415_pll_rates[] __initconst = {
- PLL_35XX_RATE(1600000000, 400, 3, 1),
- PLL_35XX_RATE(1500000000, 250, 2, 1),
- PLL_35XX_RATE(1400000000, 175, 3, 0),
- PLL_35XX_RATE(1300000000, 325, 3, 1),
- PLL_35XX_RATE(1200000000, 400, 4, 1),
- PLL_35XX_RATE(1100000000, 275, 3, 1),
- PLL_35XX_RATE(1066000000, 533, 6, 1),
- PLL_35XX_RATE(1000000000, 250, 3, 1),
- PLL_35XX_RATE(960000000, 320, 4, 1),
- PLL_35XX_RATE(900000000, 300, 4, 1),
- PLL_35XX_RATE(850000000, 425, 6, 1),
- PLL_35XX_RATE(800000000, 200, 3, 1),
- PLL_35XX_RATE(700000000, 175, 3, 1),
- PLL_35XX_RATE(667000000, 667, 12, 1),
- PLL_35XX_RATE(600000000, 400, 4, 2),
- PLL_35XX_RATE(550000000, 275, 3, 2),
- PLL_35XX_RATE(533000000, 533, 6, 2),
- PLL_35XX_RATE(520000000, 260, 3, 2),
- PLL_35XX_RATE(500000000, 250, 3, 2),
- PLL_35XX_RATE(440000000, 220, 3, 2),
- PLL_35XX_RATE(400000000, 200, 3, 2),
- PLL_35XX_RATE(350000000, 175, 3, 2),
- PLL_35XX_RATE(300000000, 300, 3, 3),
- PLL_35XX_RATE(266000000, 266, 3, 3),
- PLL_35XX_RATE(200000000, 200, 3, 3),
- PLL_35XX_RATE(160000000, 160, 3, 3),
- PLL_35XX_RATE(100000000, 200, 3, 4),
- { /* sentinel */ }
-};
-
-/* EPLL */
-static const struct samsung_pll_rate_table exynos4415_epll_rates[] __initconst = {
- PLL_36XX_RATE(800000000, 200, 3, 1, 0),
- PLL_36XX_RATE(288000000, 96, 2, 2, 0),
- PLL_36XX_RATE(192000000, 128, 2, 3, 0),
- PLL_36XX_RATE(144000000, 96, 2, 3, 0),
- PLL_36XX_RATE(96000000, 128, 2, 4, 0),
- PLL_36XX_RATE(84000000, 112, 2, 4, 0),
- PLL_36XX_RATE(80750011, 107, 2, 4, 43691),
- PLL_36XX_RATE(73728004, 98, 2, 4, 19923),
- PLL_36XX_RATE(67987602, 271, 3, 5, 62285),
- PLL_36XX_RATE(65911004, 175, 2, 5, 49982),
- PLL_36XX_RATE(50000000, 200, 3, 5, 0),
- PLL_36XX_RATE(49152003, 131, 2, 5, 4719),
- PLL_36XX_RATE(48000000, 128, 2, 5, 0),
- PLL_36XX_RATE(45250000, 181, 3, 5, 0),
- { /* sentinel */ }
-};
-
-static const struct samsung_pll_clock exynos4415_plls[] __initconst = {
- PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll",
- APLL_LOCK, APLL_CON0, exynos4415_pll_rates),
- PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll",
- EPLL_LOCK, EPLL_CON0, exynos4415_epll_rates),
- PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll", "mout_g3d_pllsrc",
- G3D_PLL_LOCK, G3D_PLL_CON0, exynos4415_pll_rates),
- PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "fin_pll",
- ISP_PLL_LOCK, ISP_PLL_CON0, exynos4415_pll_rates),
- PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll",
- "fin_pll", DISP_PLL_LOCK, DISP_PLL_CON0, exynos4415_pll_rates),
-};
-
-static const struct samsung_cmu_info cmu_info __initconst = {
- .pll_clks = exynos4415_plls,
- .nr_pll_clks = ARRAY_SIZE(exynos4415_plls),
- .mux_clks = exynos4415_mux_clks,
- .nr_mux_clks = ARRAY_SIZE(exynos4415_mux_clks),
- .div_clks = exynos4415_div_clks,
- .nr_div_clks = ARRAY_SIZE(exynos4415_div_clks),
- .gate_clks = exynos4415_gate_clks,
- .nr_gate_clks = ARRAY_SIZE(exynos4415_gate_clks),
- .fixed_clks = exynos4415_fixed_rate_clks,
- .nr_fixed_clks = ARRAY_SIZE(exynos4415_fixed_rate_clks),
- .fixed_factor_clks = exynos4415_fixed_factor_clks,
- .nr_fixed_factor_clks = ARRAY_SIZE(exynos4415_fixed_factor_clks),
- .nr_clk_ids = CLK_NR_CLKS,
- .clk_regs = exynos4415_cmu_clk_regs,
- .nr_clk_regs = ARRAY_SIZE(exynos4415_cmu_clk_regs),
-};
-
-static void __init exynos4415_cmu_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &cmu_info);
-}
-CLK_OF_DECLARE(exynos4415_cmu, "samsung,exynos4415-cmu", exynos4415_cmu_init);
-
-/*
- * CMU DMC
- */
-
-#define MPLL_LOCK 0x008
-#define MPLL_CON0 0x108
-#define MPLL_CON1 0x10c
-#define MPLL_CON2 0x110
-#define BPLL_LOCK 0x118
-#define BPLL_CON0 0x218
-#define BPLL_CON1 0x21c
-#define BPLL_CON2 0x220
-#define SRC_DMC 0x300
-#define DIV_DMC1 0x504
-
-static const unsigned long exynos4415_cmu_dmc_clk_regs[] __initconst = {
- MPLL_LOCK,
- MPLL_CON0,
- MPLL_CON1,
- MPLL_CON2,
- BPLL_LOCK,
- BPLL_CON0,
- BPLL_CON1,
- BPLL_CON2,
- SRC_DMC,
- DIV_DMC1,
-};
-
-PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", };
-PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", };
-PNAME(mbpll_p) = { "mout_mpll", "mout_bpll", };
-
-static const struct samsung_mux_clock exynos4415_dmc_mux_clks[] __initconst = {
- MUX(CLK_DMC_MOUT_MPLL, "mout_mpll", mout_mpll_p, SRC_DMC, 12, 1),
- MUX(CLK_DMC_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_DMC, 10, 1),
- MUX(CLK_DMC_MOUT_DPHY, "mout_dphy", mbpll_p, SRC_DMC, 8, 1),
- MUX(CLK_DMC_MOUT_DMC_BUS, "mout_dmc_bus", mbpll_p, SRC_DMC, 4, 1),
-};
-
-static const struct samsung_div_clock exynos4415_dmc_div_clks[] __initconst = {
- DIV(CLK_DMC_DIV_DMC, "div_dmc", "div_dmc_pre", DIV_DMC1, 27, 3),
- DIV(CLK_DMC_DIV_DPHY, "div_dphy", "mout_dphy", DIV_DMC1, 23, 3),
- DIV(CLK_DMC_DIV_DMC_PRE, "div_dmc_pre", "mout_dmc_bus",
- DIV_DMC1, 19, 2),
- DIV(CLK_DMC_DIV_DMCP, "div_dmcp", "div_dmcd", DIV_DMC1, 15, 3),
- DIV(CLK_DMC_DIV_DMCD, "div_dmcd", "div_dmc", DIV_DMC1, 11, 3),
- DIV(CLK_DMC_DIV_MPLL_PRE, "div_mpll_pre", "mout_mpll", DIV_DMC1, 8, 2),
-};
-
-static const struct samsung_pll_clock exynos4415_dmc_plls[] __initconst = {
- PLL(pll_35xx, CLK_DMC_FOUT_MPLL, "fout_mpll", "fin_pll",
- MPLL_LOCK, MPLL_CON0, exynos4415_pll_rates),
- PLL(pll_35xx, CLK_DMC_FOUT_BPLL, "fout_bpll", "fin_pll",
- BPLL_LOCK, BPLL_CON0, exynos4415_pll_rates),
-};
-
-static const struct samsung_cmu_info cmu_dmc_info __initconst = {
- .pll_clks = exynos4415_dmc_plls,
- .nr_pll_clks = ARRAY_SIZE(exynos4415_dmc_plls),
- .mux_clks = exynos4415_dmc_mux_clks,
- .nr_mux_clks = ARRAY_SIZE(exynos4415_dmc_mux_clks),
- .div_clks = exynos4415_dmc_div_clks,
- .nr_div_clks = ARRAY_SIZE(exynos4415_dmc_div_clks),
- .nr_clk_ids = NR_CLKS_DMC,
- .clk_regs = exynos4415_cmu_dmc_clk_regs,
- .nr_clk_regs = ARRAY_SIZE(exynos4415_cmu_dmc_clk_regs),
-};
-
-static void __init exynos4415_cmu_dmc_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &cmu_dmc_info);
-}
-CLK_OF_DECLARE(exynos4415_cmu_dmc, "samsung,exynos4415-cmu-dmc",
- exynos4415_cmu_dmc_init);
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 8c8b495cbf0d..cdc092a1d9ef 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -586,7 +586,7 @@ static const struct samsung_gate_clock exynos5800_gate_clks[] __initconst = {
GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam",
GATE_BUS_TOP, 24, 0, 0),
GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler",
- GATE_BUS_TOP, 27, 0, 0),
+ GATE_BUS_TOP, 27, CLK_IS_CRITICAL, 0),
};
static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
@@ -956,20 +956,20 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk333_g2d", GATE_IP_G2D, 7, 0, 0),
GATE(0, "aclk200_fsys", "mout_user_aclk200_fsys",
- GATE_BUS_FSYS0, 9, CLK_IGNORE_UNUSED, 0),
+ GATE_BUS_FSYS0, 9, CLK_IS_CRITICAL, 0),
GATE(0, "aclk200_fsys2", "mout_user_aclk200_fsys2",
GATE_BUS_FSYS0, 10, CLK_IGNORE_UNUSED, 0),
GATE(0, "aclk333_g2d", "mout_user_aclk333_g2d",
GATE_BUS_TOP, 0, CLK_IGNORE_UNUSED, 0),
GATE(0, "aclk266_g2d", "mout_user_aclk266_g2d",
- GATE_BUS_TOP, 1, CLK_IGNORE_UNUSED, 0),
+ GATE_BUS_TOP, 1, CLK_IS_CRITICAL, 0),
GATE(0, "aclk300_jpeg", "mout_user_aclk300_jpeg",
GATE_BUS_TOP, 4, CLK_IGNORE_UNUSED, 0),
GATE(0, "aclk333_432_isp0", "mout_user_aclk333_432_isp0",
GATE_BUS_TOP, 5, 0, 0),
GATE(0, "aclk300_gscl", "mout_user_aclk300_gscl",
- GATE_BUS_TOP, 6, CLK_IGNORE_UNUSED, 0),
+ GATE_BUS_TOP, 6, CLK_IS_CRITICAL, 0),
GATE(0, "aclk333_432_gscl", "mout_user_aclk333_432_gscl",
GATE_BUS_TOP, 7, CLK_IGNORE_UNUSED, 0),
GATE(0, "aclk333_432_isp", "mout_user_aclk333_432_isp",
@@ -983,20 +983,20 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(0, "aclk166", "mout_user_aclk166",
GATE_BUS_TOP, 14, CLK_IGNORE_UNUSED, 0),
GATE(CLK_ACLK333, "aclk333", "mout_user_aclk333",
- GATE_BUS_TOP, 15, CLK_IGNORE_UNUSED, 0),
+ GATE_BUS_TOP, 15, CLK_IS_CRITICAL, 0),
GATE(0, "aclk400_isp", "mout_user_aclk400_isp",
GATE_BUS_TOP, 16, 0, 0),
GATE(0, "aclk400_mscl", "mout_user_aclk400_mscl",
GATE_BUS_TOP, 17, 0, 0),
GATE(0, "aclk200_disp1", "mout_user_aclk200_disp1",
- GATE_BUS_TOP, 18, 0, 0),
+ GATE_BUS_TOP, 18, CLK_IS_CRITICAL, 0),
GATE(CLK_SCLK_MPHY_IXTAL24, "sclk_mphy_ixtal24", "mphy_refclk_ixtal24",
GATE_BUS_TOP, 28, 0, 0),
GATE(CLK_SCLK_HSIC_12M, "sclk_hsic_12m", "ff_hsic_12m",
GATE_BUS_TOP, 29, 0, 0),
GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1",
- SRC_MASK_TOP2, 24, 0, 0),
+ SRC_MASK_TOP2, 24, CLK_IS_CRITICAL, 0),
GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk",
SRC_MASK_TOP7, 20, 0, 0),
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index f096bd7df40c..11343a597093 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -6,7 +6,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * Common Clock Framework support for Exynos5443 SoC.
+ * Common Clock Framework support for Exynos5433 SoC.
*/
#include <linux/clk-provider.h>
@@ -549,10 +549,10 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = {
29, CLK_IGNORE_UNUSED, 0),
GATE(CLK_ACLK_BUS0_400, "aclk_bus0_400", "div_aclk_bus0_400",
ENABLE_ACLK_TOP, 26,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
GATE(CLK_ACLK_BUS1_400, "aclk_bus1_400", "div_aclk_bus1_400",
ENABLE_ACLK_TOP, 25,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266",
ENABLE_ACLK_TOP, 24,
CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
@@ -616,7 +616,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = {
/* ENABLE_SCLK_TOP_MSCL */
GATE(CLK_SCLK_JPEG_MSCL, "sclk_jpeg_mscl", "div_sclk_jpeg",
- ENABLE_SCLK_TOP_MSCL, 0, 0, 0),
+ ENABLE_SCLK_TOP_MSCL, 0, CLK_SET_RATE_PARENT, 0),
/* ENABLE_SCLK_TOP_CAM1 */
GATE(CLK_SCLK_ISP_SENSOR2, "sclk_isp_sensor2", "div_sclk_isp_sensor2_b",
@@ -698,7 +698,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = {
* ATLAS_PLL & APOLLO_PLL & MEM0_PLL & MEM1_PLL & BUS_PLL & MFC_PLL
* & MPHY_PLL & G3D_PLL & DISP_PLL & ISP_PLL
*/
-static const struct samsung_pll_rate_table exynos5443_pll_rates[] __initconst = {
+static const struct samsung_pll_rate_table exynos5433_pll_rates[] __initconst = {
PLL_35XX_RATE(2500000000U, 625, 6, 0),
PLL_35XX_RATE(2400000000U, 500, 5, 0),
PLL_35XX_RATE(2300000000U, 575, 6, 0),
@@ -739,7 +739,9 @@ static const struct samsung_pll_rate_table exynos5443_pll_rates[] __initconst =
PLL_35XX_RATE(350000000U, 350, 6, 2),
PLL_35XX_RATE(333000000U, 222, 4, 2),
PLL_35XX_RATE(300000000U, 500, 5, 3),
+ PLL_35XX_RATE(278000000U, 556, 6, 3),
PLL_35XX_RATE(266000000U, 532, 6, 3),
+ PLL_35XX_RATE(250000000U, 500, 6, 3),
PLL_35XX_RATE(200000000U, 400, 6, 3),
PLL_35XX_RATE(166000000U, 332, 6, 3),
PLL_35XX_RATE(160000000U, 320, 6, 3),
@@ -749,7 +751,7 @@ static const struct samsung_pll_rate_table exynos5443_pll_rates[] __initconst =
};
/* AUD_PLL */
-static const struct samsung_pll_rate_table exynos5443_aud_pll_rates[] __initconst = {
+static const struct samsung_pll_rate_table exynos5433_aud_pll_rates[] __initconst = {
PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
PLL_36XX_RATE(393216000U, 197, 3, 2, -25690),
PLL_36XX_RATE(384000000U, 128, 2, 2, 0),
@@ -764,9 +766,9 @@ static const struct samsung_pll_rate_table exynos5443_aud_pll_rates[] __initcons
static const struct samsung_pll_clock top_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "oscclk",
- ISP_PLL_LOCK, ISP_PLL_CON0, exynos5443_pll_rates),
+ ISP_PLL_LOCK, ISP_PLL_CON0, exynos5433_pll_rates),
PLL(pll_36xx, CLK_FOUT_AUD_PLL, "fout_aud_pll", "oscclk",
- AUD_PLL_LOCK, AUD_PLL_CON0, exynos5443_aud_pll_rates),
+ AUD_PLL_LOCK, AUD_PLL_CON0, exynos5433_aud_pll_rates),
};
static const struct samsung_cmu_info top_cmu_info __initconst = {
@@ -820,7 +822,7 @@ PNAME(mout_mphy_pll_p) = { "oscclk", "fout_mphy_pll", };
static const struct samsung_pll_clock cpif_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_MPHY_PLL, "fout_mphy_pll", "oscclk",
- MPHY_PLL_LOCK, MPHY_PLL_CON0, exynos5443_pll_rates),
+ MPHY_PLL_LOCK, MPHY_PLL_CON0, exynos5433_pll_rates),
};
static const struct samsung_mux_clock cpif_mux_clks[] __initconst = {
@@ -1011,13 +1013,13 @@ static const unsigned long mif_clk_regs[] __initconst = {
static const struct samsung_pll_clock mif_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_MEM0_PLL, "fout_mem0_pll", "oscclk",
- MEM0_PLL_LOCK, MEM0_PLL_CON0, exynos5443_pll_rates),
+ MEM0_PLL_LOCK, MEM0_PLL_CON0, exynos5433_pll_rates),
PLL(pll_35xx, CLK_FOUT_MEM1_PLL, "fout_mem1_pll", "oscclk",
- MEM1_PLL_LOCK, MEM1_PLL_CON0, exynos5443_pll_rates),
+ MEM1_PLL_LOCK, MEM1_PLL_CON0, exynos5433_pll_rates),
PLL(pll_35xx, CLK_FOUT_BUS_PLL, "fout_bus_pll", "oscclk",
- BUS_PLL_LOCK, BUS_PLL_CON0, exynos5443_pll_rates),
+ BUS_PLL_LOCK, BUS_PLL_CON0, exynos5433_pll_rates),
PLL(pll_35xx, CLK_FOUT_MFC_PLL, "fout_mfc_pll", "oscclk",
- MFC_PLL_LOCK, MFC_PLL_CON0, exynos5443_pll_rates),
+ MFC_PLL_LOCK, MFC_PLL_CON0, exynos5433_pll_rates),
};
/* list of all parent clock list */
@@ -1382,7 +1384,7 @@ static const struct samsung_gate_clock mif_gate_clks[] __initconst = {
/* ENABLE_ACLK_MIF3 */
GATE(CLK_ACLK_BUS2_400, "aclk_bus2_400", "div_aclk_bus2_400",
ENABLE_ACLK_MIF3, 4,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
GATE(CLK_ACLK_DISP_333, "aclk_disp_333", "div_aclk_disp_333",
ENABLE_ACLK_MIF3, 1,
CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
@@ -2539,7 +2541,7 @@ PNAME(mout_sclk_decon_tv_vclk_b_disp_p) = { "mout_sclk_decon_tv_vclk_a_disp",
static const struct samsung_pll_clock disp_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll", "oscclk",
- DISP_PLL_LOCK, DISP_PLL_CON0, exynos5443_pll_rates),
+ DISP_PLL_LOCK, DISP_PLL_CON0, exynos5433_pll_rates),
};
static const struct samsung_fixed_factor_clock disp_fixed_factor_clks[] __initconst = {
@@ -2559,8 +2561,10 @@ static const struct samsung_fixed_rate_clock disp_fixed_clks[] __initconst = {
FRATE(0, "phyclk_mipidphy1_bitclkdiv8_phy", NULL, 0, 188000000),
FRATE(0, "phyclk_mipidphy1_rxclkesc0_phy", NULL, 0, 100000000),
/* PHY clocks from MIPI_DPHY0 */
- FRATE(0, "phyclk_mipidphy0_bitclkdiv8_phy", NULL, 0, 188000000),
- FRATE(0, "phyclk_mipidphy0_rxclkesc0_phy", NULL, 0, 100000000),
+ FRATE(CLK_PHYCLK_MIPIDPHY0_BITCLKDIV8_PHY, "phyclk_mipidphy0_bitclkdiv8_phy",
+ NULL, 0, 188000000),
+ FRATE(CLK_PHYCLK_MIPIDPHY0_RXCLKESC0_PHY, "phyclk_mipidphy0_rxclkesc0_phy",
+ NULL, 0, 100000000),
/* PHY clocks from HDMI_PHY */
FRATE(CLK_PHYCLK_HDMIPHY_TMDS_CLKO_PHY, "phyclk_hdmiphy_tmds_clko_phy",
NULL, 0, 300000000),
@@ -3224,7 +3228,7 @@ PNAME(mout_g3d_pll_p) = { "oscclk", "fout_g3d_pll", };
static const struct samsung_pll_clock g3d_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll", "oscclk",
- G3D_PLL_LOCK, G3D_PLL_CON0, exynos5443_pll_rates),
+ G3D_PLL_LOCK, G3D_PLL_CON0, exynos5433_pll_rates),
};
static const struct samsung_mux_clock g3d_mux_clks[] __initconst = {
@@ -3514,7 +3518,7 @@ PNAME(mout_apollo_p) = { "mout_apollo_pll",
static const struct samsung_pll_clock apollo_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_APOLLO_PLL, "fout_apollo_pll", "oscclk",
- APOLLO_PLL_LOCK, APOLLO_PLL_CON0, exynos5443_pll_rates),
+ APOLLO_PLL_LOCK, APOLLO_PLL_CON0, exynos5433_pll_rates),
};
static const struct samsung_mux_clock apollo_mux_clks[] __initconst = {
@@ -3737,7 +3741,7 @@ PNAME(mout_atlas_p) = { "mout_atlas_pll",
static const struct samsung_pll_clock atlas_pll_clks[] __initconst = {
PLL(pll_35xx, CLK_FOUT_ATLAS_PLL, "fout_atlas_pll", "oscclk",
- ATLAS_PLL_LOCK, ATLAS_PLL_CON0, exynos5443_pll_rates),
+ ATLAS_PLL_LOCK, ATLAS_PLL_CON0, exynos5433_pll_rates),
};
static const struct samsung_mux_clock atlas_mux_clks[] __initconst = {
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 9617825daabb..52290894857a 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -136,11 +136,39 @@ static const struct clk_ops samsung_pll3000_clk_ops = {
#define PLL35XX_MDIV_MASK (0x3FF)
#define PLL35XX_PDIV_MASK (0x3F)
#define PLL35XX_SDIV_MASK (0x7)
-#define PLL35XX_LOCK_STAT_MASK (0x1)
#define PLL35XX_MDIV_SHIFT (16)
#define PLL35XX_PDIV_SHIFT (8)
#define PLL35XX_SDIV_SHIFT (0)
#define PLL35XX_LOCK_STAT_SHIFT (29)
+#define PLL35XX_ENABLE_SHIFT (31)
+
+static int samsung_pll35xx_enable(struct clk_hw *hw)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 tmp;
+
+ tmp = readl_relaxed(pll->con_reg);
+ tmp |= BIT(PLL35XX_ENABLE_SHIFT);
+ writel_relaxed(tmp, pll->con_reg);
+
+ /* wait_lock_time */
+ do {
+ cpu_relax();
+ tmp = readl_relaxed(pll->con_reg);
+ } while (!(tmp & BIT(PLL35XX_LOCK_STAT_SHIFT)));
+
+ return 0;
+}
+
+static void samsung_pll35xx_disable(struct clk_hw *hw)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 tmp;
+
+ tmp = readl_relaxed(pll->con_reg);
+ tmp &= ~BIT(PLL35XX_ENABLE_SHIFT);
+ writel_relaxed(tmp, pll->con_reg);
+}
static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -210,12 +238,13 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate,
(rate->sdiv << PLL35XX_SDIV_SHIFT);
writel_relaxed(tmp, pll->con_reg);
- /* wait_lock_time */
- do {
- cpu_relax();
- tmp = readl_relaxed(pll->con_reg);
- } while (!(tmp & (PLL35XX_LOCK_STAT_MASK
- << PLL35XX_LOCK_STAT_SHIFT)));
+ /* wait_lock_time if enabled */
+ if (tmp & BIT(PLL35XX_ENABLE_SHIFT)) {
+ do {
+ cpu_relax();
+ tmp = readl_relaxed(pll->con_reg);
+ } while (!(tmp & BIT(PLL35XX_LOCK_STAT_SHIFT)));
+ }
return 0;
}
@@ -223,6 +252,8 @@ static const struct clk_ops samsung_pll35xx_clk_ops = {
.recalc_rate = samsung_pll35xx_recalc_rate,
.round_rate = samsung_pll_round_rate,
.set_rate = samsung_pll35xx_set_rate,
+ .enable = samsung_pll35xx_enable,
+ .disable = samsung_pll35xx_disable,
};
static const struct clk_ops samsung_pll35xx_clk_min_ops = {
diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
index d7a1e772d95a..e0650c33863b 100644
--- a/drivers/clk/samsung/clk-s3c2410.c
+++ b/drivers/clk/samsung/clk-s3c2410.c
@@ -76,7 +76,7 @@ static struct syscore_ops s3c2410_clk_syscore_ops = {
.resume = s3c2410_clk_resume,
};
-static void s3c2410_clk_sleep_init(void)
+static void __init s3c2410_clk_sleep_init(void)
{
s3c2410_save = samsung_clk_alloc_reg_dump(s3c2410_clk_regs,
ARRAY_SIZE(s3c2410_clk_regs));
@@ -90,7 +90,7 @@ static void s3c2410_clk_sleep_init(void)
return;
}
#else
-static void s3c2410_clk_sleep_init(void) {}
+static void __init s3c2410_clk_sleep_init(void) {}
#endif
PNAME(fclk_p) = { "mpll", "div_slow" };
diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c
index ec873ee15d37..b8340a49921b 100644
--- a/drivers/clk/samsung/clk-s3c2412.c
+++ b/drivers/clk/samsung/clk-s3c2412.c
@@ -69,7 +69,7 @@ static struct syscore_ops s3c2412_clk_syscore_ops = {
.resume = s3c2412_clk_resume,
};
-static void s3c2412_clk_sleep_init(void)
+static void __init s3c2412_clk_sleep_init(void)
{
s3c2412_save = samsung_clk_alloc_reg_dump(s3c2412_clk_regs,
ARRAY_SIZE(s3c2412_clk_regs));
@@ -83,7 +83,7 @@ static void s3c2412_clk_sleep_init(void)
return;
}
#else
-static void s3c2412_clk_sleep_init(void) {}
+static void __init s3c2412_clk_sleep_init(void) {}
#endif
static struct clk_div_table divxti_d[] = {
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
index 5e24a17e10e6..abb935c42916 100644
--- a/drivers/clk/samsung/clk-s3c2443.c
+++ b/drivers/clk/samsung/clk-s3c2443.c
@@ -89,7 +89,7 @@ static struct syscore_ops s3c2443_clk_syscore_ops = {
.resume = s3c2443_clk_resume,
};
-static void s3c2443_clk_sleep_init(void)
+static void __init s3c2443_clk_sleep_init(void)
{
s3c2443_save = samsung_clk_alloc_reg_dump(s3c2443_clk_regs,
ARRAY_SIZE(s3c2443_clk_regs));
@@ -103,7 +103,7 @@ static void s3c2443_clk_sleep_init(void)
return;
}
#else
-static void s3c2443_clk_sleep_init(void) {}
+static void __init s3c2443_clk_sleep_init(void) {}
#endif
PNAME(epllref_p) = { "mpllref", "mpllref", "xti", "ext" };
diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c
index a48bd5f17330..7306867a0ab8 100644
--- a/drivers/clk/samsung/clk-s3c64xx.c
+++ b/drivers/clk/samsung/clk-s3c64xx.c
@@ -121,7 +121,7 @@ static struct syscore_ops s3c64xx_clk_syscore_ops = {
.resume = s3c64xx_clk_resume,
};
-static void s3c64xx_clk_sleep_init(void)
+static void __init s3c64xx_clk_sleep_init(void)
{
s3c64xx_save_common = samsung_clk_alloc_reg_dump(s3c64xx_clk_regs,
ARRAY_SIZE(s3c64xx_clk_regs));
@@ -145,7 +145,7 @@ err_warn:
__func__);
}
#else
-static void s3c64xx_clk_sleep_init(void) {}
+static void __init s3c64xx_clk_sleep_init(void) {}
#endif
/* List of parent clocks common for all S3C64xx SoCs. */
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 8454c6e3dd65..67659091860d 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -64,6 +64,16 @@ config SUN50I_A64_CCU
select SUNXI_CCU_PHASE
default ARM64 && ARCH_SUNXI
+config SUN5I_CCU
+ bool "Support for the Allwinner sun5i family CCM"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_NK
+ select SUNXI_CCU_NKM
+ select SUNXI_CCU_NM
+ select SUNXI_CCU_MP
+ select SUNXI_CCU_PHASE
+ default MACH_SUN5I
+
config SUN6I_A31_CCU
bool "Support for the Allwinner A31/A31s CCU"
select SUNXI_CCU_DIV
@@ -109,4 +119,25 @@ config SUN8I_H3_CCU
select SUNXI_CCU_PHASE
default MACH_SUN8I
+config SUN8I_V3S_CCU
+ bool "Support for the Allwinner V3s CCU"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_NK
+ select SUNXI_CCU_NKM
+ select SUNXI_CCU_NKMP
+ select SUNXI_CCU_NM
+ select SUNXI_CCU_MP
+ select SUNXI_CCU_PHASE
+ default MACH_SUN8I
+
+config SUN9I_A80_CCU
+ bool "Support for the Allwinner A80 CCU"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_GATE
+ select SUNXI_CCU_NKMP
+ select SUNXI_CCU_NM
+ select SUNXI_CCU_MP
+ select SUNXI_CCU_PHASE
+ default MACH_SUN9I
+
endif
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 24fbc6e5deb8..6feaac0c5600 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -19,7 +19,12 @@ obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o
# SoC support
obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o
+obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o
obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o
obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o
obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o
obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o
+obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o
+obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o
+obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o
+obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c
new file mode 100644
index 000000000000..06edaa523479
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun5i.c
@@ -0,0 +1,1022 @@
+/*
+ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun5i.h"
+
+static struct ccu_nkmp pll_core_clk = {
+ .enable = BIT(31),
+ .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0),
+ .k = _SUNXI_CCU_MULT(4, 2),
+ .m = _SUNXI_CCU_DIV(0, 2),
+ .p = _SUNXI_CCU_DIV(16, 2),
+ .common = {
+ .reg = 0x000,
+ .hw.init = CLK_HW_INIT("pll-core",
+ "hosc",
+ &ccu_nkmp_ops,
+ 0),
+ },
+};
+
+/*
+ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
+ * the base (2x, 4x and 8x), and one variable divider (the one true
+ * pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names
+ */
+#define SUN5I_PLL_AUDIO_REG 0x008
+
+static struct ccu_nm pll_audio_base_clk = {
+ .enable = BIT(31),
+ .n = _SUNXI_CCU_MULT_OFFSET(8, 7, 0),
+
+ /*
+ * The datasheet is wrong here, this doesn't have any
+ * offset
+ */
+ .m = _SUNXI_CCU_DIV_OFFSET(0, 5, 0),
+ .common = {
+ .reg = 0x008,
+ .hw.init = CLK_HW_INIT("pll-audio-base",
+ "hosc",
+ &ccu_nm_ops,
+ 0),
+ },
+};
+
+static struct ccu_mult pll_video0_clk = {
+ .enable = BIT(31),
+ .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127),
+ .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14),
+ 270000000, 297000000),
+ .common = {
+ .reg = 0x010,
+ .features = (CCU_FEATURE_FRACTIONAL |
+ CCU_FEATURE_ALL_PREDIV),
+ .prediv = 8,
+ .hw.init = CLK_HW_INIT("pll-video0",
+ "hosc",
+ &ccu_mult_ops,
+ 0),
+ },
+};
+
+static struct ccu_nkmp pll_ve_clk = {
+ .enable = BIT(31),
+ .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0),
+ .k = _SUNXI_CCU_MULT(4, 2),
+ .m = _SUNXI_CCU_DIV(0, 2),
+ .p = _SUNXI_CCU_DIV(16, 2),
+ .common = {
+ .reg = 0x018,
+ .hw.init = CLK_HW_INIT("pll-ve",
+ "hosc",
+ &ccu_nkmp_ops,
+ 0),
+ },
+};
+
+static struct ccu_nk pll_ddr_base_clk = {
+ .enable = BIT(31),
+ .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0),
+ .k = _SUNXI_CCU_MULT(4, 2),
+ .common = {
+ .reg = 0x020,
+ .hw.init = CLK_HW_INIT("pll-ddr-base",
+ "hosc",
+ &ccu_nk_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_M(pll_ddr_clk, "pll-ddr", "pll-ddr-base", 0x020, 0, 2,
+ CLK_IS_CRITICAL);
+
+static struct ccu_div pll_ddr_other_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(16, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .common = {
+ .reg = 0x020,
+ .hw.init = CLK_HW_INIT("pll-ddr-other", "pll-ddr-base",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_nk pll_periph_clk = {
+ .enable = BIT(31),
+ .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0),
+ .k = _SUNXI_CCU_MULT(4, 2),
+ .fixed_post_div = 2,
+ .common = {
+ .reg = 0x028,
+ .features = CCU_FEATURE_FIXED_POSTDIV,
+ .hw.init = CLK_HW_INIT("pll-periph",
+ "hosc",
+ &ccu_nk_ops,
+ 0),
+ },
+};
+
+static struct ccu_mult pll_video1_clk = {
+ .enable = BIT(31),
+ .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127),
+ .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14),
+ 270000000, 297000000),
+ .common = {
+ .reg = 0x030,
+ .features = (CCU_FEATURE_FRACTIONAL |
+ CCU_FEATURE_ALL_PREDIV),
+ .prediv = 8,
+ .hw.init = CLK_HW_INIT("pll-video1",
+ "hosc",
+ &ccu_mult_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(hosc_clk, "hosc", "osc24M", 0x050, BIT(0), 0);
+
+#define SUN5I_AHB_REG 0x054
+static const char * const cpu_parents[] = { "osc32k", "hosc",
+ "pll-core" , "pll-periph" };
+static const struct ccu_mux_fixed_prediv cpu_predivs[] = {
+ { .index = 3, .div = 3, },
+};
+static struct ccu_mux cpu_clk = {
+ .mux = {
+ .shift = 16,
+ .width = 2,
+ .fixed_predivs = cpu_predivs,
+ .n_predivs = ARRAY_SIZE(cpu_predivs),
+ },
+ .common = {
+ .reg = 0x054,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("cpu",
+ cpu_parents,
+ &ccu_mux_ops,
+ CLK_IS_CRITICAL),
+ }
+};
+
+static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x054, 0, 2, 0);
+
+static const char * const ahb_parents[] = { "axi" , "cpu", "pll-periph" };
+static const struct ccu_mux_fixed_prediv ahb_predivs[] = {
+ { .index = 2, .div = 2, },
+};
+static struct ccu_div ahb_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = {
+ .shift = 6,
+ .width = 2,
+ .fixed_predivs = ahb_predivs,
+ .n_predivs = ARRAY_SIZE(ahb_predivs),
+ },
+
+ .common = {
+ .reg = 0x054,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb",
+ ahb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct clk_div_table apb0_div_table[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { /* Sentinel */ },
+};
+static SUNXI_CCU_DIV_TABLE(apb0_clk, "apb0", "ahb",
+ 0x054, 8, 2, apb0_div_table, 0);
+
+static const char * const apb1_parents[] = { "hosc", "pll-periph", "osc32k" };
+static SUNXI_CCU_MP_WITH_MUX(apb1_clk, "apb1", apb1_parents, 0x058,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static SUNXI_CCU_GATE(axi_dram_clk, "axi-dram", "axi",
+ 0x05c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(ahb_otg_clk, "ahb-otg", "ahb",
+ 0x060, BIT(0), 0);
+static SUNXI_CCU_GATE(ahb_ehci_clk, "ahb-ehci", "ahb",
+ 0x060, BIT(1), 0);
+static SUNXI_CCU_GATE(ahb_ohci_clk, "ahb-ohci", "ahb",
+ 0x060, BIT(2), 0);
+static SUNXI_CCU_GATE(ahb_ss_clk, "ahb-ss", "ahb",
+ 0x060, BIT(5), 0);
+static SUNXI_CCU_GATE(ahb_dma_clk, "ahb-dma", "ahb",
+ 0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(ahb_bist_clk, "ahb-bist", "ahb",
+ 0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(ahb_mmc0_clk, "ahb-mmc0", "ahb",
+ 0x060, BIT(8), 0);
+static SUNXI_CCU_GATE(ahb_mmc1_clk, "ahb-mmc1", "ahb",
+ 0x060, BIT(9), 0);
+static SUNXI_CCU_GATE(ahb_mmc2_clk, "ahb-mmc2", "ahb",
+ 0x060, BIT(10), 0);
+static SUNXI_CCU_GATE(ahb_nand_clk, "ahb-nand", "ahb",
+ 0x060, BIT(13), 0);
+static SUNXI_CCU_GATE(ahb_sdram_clk, "ahb-sdram", "ahb",
+ 0x060, BIT(14), CLK_IS_CRITICAL);
+static SUNXI_CCU_GATE(ahb_emac_clk, "ahb-emac", "ahb",
+ 0x060, BIT(17), 0);
+static SUNXI_CCU_GATE(ahb_ts_clk, "ahb-ts", "ahb",
+ 0x060, BIT(18), 0);
+static SUNXI_CCU_GATE(ahb_spi0_clk, "ahb-spi0", "ahb",
+ 0x060, BIT(20), 0);
+static SUNXI_CCU_GATE(ahb_spi1_clk, "ahb-spi1", "ahb",
+ 0x060, BIT(21), 0);
+static SUNXI_CCU_GATE(ahb_spi2_clk, "ahb-spi2", "ahb",
+ 0x060, BIT(22), 0);
+static SUNXI_CCU_GATE(ahb_gps_clk, "ahb-gps", "ahb",
+ 0x060, BIT(26), 0);
+static SUNXI_CCU_GATE(ahb_hstimer_clk, "ahb-hstimer", "ahb",
+ 0x060, BIT(28), 0);
+
+static SUNXI_CCU_GATE(ahb_ve_clk, "ahb-ve", "ahb",
+ 0x064, BIT(0), 0);
+static SUNXI_CCU_GATE(ahb_tve_clk, "ahb-tve", "ahb",
+ 0x064, BIT(2), 0);
+static SUNXI_CCU_GATE(ahb_lcd_clk, "ahb-lcd", "ahb",
+ 0x064, BIT(4), 0);
+static SUNXI_CCU_GATE(ahb_csi_clk, "ahb-csi", "ahb",
+ 0x064, BIT(8), 0);
+static SUNXI_CCU_GATE(ahb_hdmi_clk, "ahb-hdmi", "ahb",
+ 0x064, BIT(11), 0);
+static SUNXI_CCU_GATE(ahb_de_be_clk, "ahb-de-be", "ahb",
+ 0x064, BIT(12), 0);
+static SUNXI_CCU_GATE(ahb_de_fe_clk, "ahb-de-fe", "ahb",
+ 0x064, BIT(14), 0);
+static SUNXI_CCU_GATE(ahb_iep_clk, "ahb-iep", "ahb",
+ 0x064, BIT(19), 0);
+static SUNXI_CCU_GATE(ahb_gpu_clk, "ahb-gpu", "ahb",
+ 0x064, BIT(20), 0);
+
+static SUNXI_CCU_GATE(apb0_codec_clk, "apb0-codec", "apb0",
+ 0x068, BIT(0), 0);
+static SUNXI_CCU_GATE(apb0_spdif_clk, "apb0-spdif", "apb0",
+ 0x068, BIT(1), 0);
+static SUNXI_CCU_GATE(apb0_i2s_clk, "apb0-i2s", "apb0",
+ 0x068, BIT(3), 0);
+static SUNXI_CCU_GATE(apb0_pio_clk, "apb0-pio", "apb0",
+ 0x068, BIT(5), 0);
+static SUNXI_CCU_GATE(apb0_ir_clk, "apb0-ir", "apb0",
+ 0x068, BIT(6), 0);
+static SUNXI_CCU_GATE(apb0_keypad_clk, "apb0-keypad", "apb0",
+ 0x068, BIT(10), 0);
+
+static SUNXI_CCU_GATE(apb1_i2c0_clk, "apb1-i2c0", "apb1",
+ 0x06c, BIT(0), 0);
+static SUNXI_CCU_GATE(apb1_i2c1_clk, "apb1-i2c1", "apb1",
+ 0x06c, BIT(1), 0);
+static SUNXI_CCU_GATE(apb1_i2c2_clk, "apb1-i2c2", "apb1",
+ 0x06c, BIT(2), 0);
+static SUNXI_CCU_GATE(apb1_uart0_clk, "apb1-uart0", "apb1",
+ 0x06c, BIT(16), 0);
+static SUNXI_CCU_GATE(apb1_uart1_clk, "apb1-uart1", "apb1",
+ 0x06c, BIT(17), 0);
+static SUNXI_CCU_GATE(apb1_uart2_clk, "apb1-uart2", "apb1",
+ 0x06c, BIT(18), 0);
+static SUNXI_CCU_GATE(apb1_uart3_clk, "apb1-uart3", "apb1",
+ 0x06c, BIT(19), 0);
+
+static const char * const mod0_default_parents[] = { "hosc", "pll-periph",
+ "pll-ddr-other" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, 0x098,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ir_clk, "ir", mod0_default_parents, 0x0b0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
+ "pll-audio-2x", "pll-audio" };
+static SUNXI_CCU_MUX_WITH_GATE(i2s_clk, "i2s", i2s_parents,
+ 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static const char * const spdif_parents[] = { "pll-audio-8x", "pll-audio-4x",
+ "pll-audio-2x", "pll-audio" };
+static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", spdif_parents,
+ 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static const char * const keypad_parents[] = { "hosc", "losc"};
+static const u8 keypad_table[] = { 0, 2 };
+static struct ccu_mp keypad_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(8, 5),
+ .p = _SUNXI_CCU_DIV(20, 2),
+ .mux = _SUNXI_CCU_MUX_TABLE(24, 2, keypad_table),
+
+ .common = {
+ .reg = 0x0c4,
+ .hw.init = CLK_HW_INIT_PARENTS("keypad",
+ keypad_parents,
+ &ccu_mp_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(usb_ohci_clk, "usb-ohci", "pll-periph",
+ 0x0cc, BIT(6), 0);
+static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "pll-periph",
+ 0x0cc, BIT(8), 0);
+static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "pll-periph",
+ 0x0cc, BIT(9), 0);
+
+static const char * const gps_parents[] = { "hosc", "pll-periph",
+ "pll-video1", "pll-ve" };
+static SUNXI_CCU_M_WITH_MUX_GATE(gps_clk, "gps", gps_parents,
+ 0x0d0, 0, 3, 24, 2, BIT(31), 0);
+
+static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "pll-ddr",
+ 0x100, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "pll-ddr",
+ 0x100, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "pll-ddr",
+ 0x100, BIT(3), 0);
+static SUNXI_CCU_GATE(dram_tve_clk, "dram-tve", "pll-ddr",
+ 0x100, BIT(5), 0);
+static SUNXI_CCU_GATE(dram_de_fe_clk, "dram-de-fe", "pll-ddr",
+ 0x100, BIT(25), 0);
+static SUNXI_CCU_GATE(dram_de_be_clk, "dram-de-be", "pll-ddr",
+ 0x100, BIT(26), 0);
+static SUNXI_CCU_GATE(dram_ace_clk, "dram-ace", "pll-ddr",
+ 0x100, BIT(29), 0);
+static SUNXI_CCU_GATE(dram_iep_clk, "dram-iep", "pll-ddr",
+ 0x100, BIT(31), 0);
+
+static const char * const de_parents[] = { "pll-video0", "pll-video1",
+ "pll-ddr-other" };
+static SUNXI_CCU_M_WITH_MUX_GATE(de_be_clk, "de-be", de_parents,
+ 0x104, 0, 4, 24, 2, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(de_fe_clk, "de-fe", de_parents,
+ 0x10c, 0, 4, 24, 2, BIT(31), 0);
+
+static const char * const tcon_parents[] = { "pll-video0", "pll-video1",
+ "pll-video0-2x", "pll-video1-2x" };
+static SUNXI_CCU_MUX_WITH_GATE(tcon_ch0_clk, "tcon-ch0-sclk", tcon_parents,
+ 0x118, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(tcon_ch1_sclk2_clk, "tcon-ch1-sclk2",
+ tcon_parents,
+ 0x12c, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_GATE(tcon_ch1_sclk1_clk, "tcon-ch1-sclk1", "tcon-ch1-sclk2",
+ 0x12c, 11, 1, BIT(15), CLK_SET_RATE_PARENT);
+
+static const char * const csi_parents[] = { "hosc", "pll-video0", "pll-video1",
+ "pll-video0-2x", "pll-video1-2x" };
+static const u8 csi_table[] = { 0, 1, 2, 5, 6 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_clk, "csi",
+ csi_parents, csi_table,
+ 0x134, 0, 5, 24, 2, BIT(31), 0);
+
+static SUNXI_CCU_GATE(ve_clk, "ve", "pll-ve",
+ 0x13c, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio",
+ 0x140, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(avs_clk, "avs", "hosc",
+ 0x144, BIT(31), 0);
+
+static const char * const hdmi_parents[] = { "pll-video0", "pll-video0-2x" };
+static const u8 hdmi_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(hdmi_clk, "hdmi",
+ hdmi_parents, hdmi_table,
+ 0x150, 0, 4, 24, 2, BIT(31),
+ CLK_SET_RATE_PARENT);
+
+static const char * const gpu_parents[] = { "pll-video0", "pll-ve",
+ "pll-ddr-other", "pll-video1",
+ "pll-video1-2x" };
+static SUNXI_CCU_M_WITH_MUX_GATE(gpu_clk, "gpu", gpu_parents,
+ 0x154, 0, 4, 24, 3, BIT(31), 0);
+
+static const char * const mbus_parents[] = { "hosc", "pll-periph", "pll-ddr" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
+ 0x15c, 0, 4, 16, 2, 24, 2, BIT(31), CLK_IS_CRITICAL);
+
+static SUNXI_CCU_GATE(iep_clk, "iep", "de-be",
+ 0x160, BIT(31), 0);
+
+static struct ccu_common *sun5i_a10s_ccu_clks[] = {
+ &hosc_clk.common,
+ &pll_core_clk.common,
+ &pll_audio_base_clk.common,
+ &pll_video0_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr_base_clk.common,
+ &pll_ddr_clk.common,
+ &pll_ddr_other_clk.common,
+ &pll_periph_clk.common,
+ &pll_video1_clk.common,
+ &cpu_clk.common,
+ &axi_clk.common,
+ &ahb_clk.common,
+ &apb0_clk.common,
+ &apb1_clk.common,
+ &axi_dram_clk.common,
+ &ahb_otg_clk.common,
+ &ahb_ehci_clk.common,
+ &ahb_ohci_clk.common,
+ &ahb_ss_clk.common,
+ &ahb_dma_clk.common,
+ &ahb_bist_clk.common,
+ &ahb_mmc0_clk.common,
+ &ahb_mmc1_clk.common,
+ &ahb_mmc2_clk.common,
+ &ahb_nand_clk.common,
+ &ahb_sdram_clk.common,
+ &ahb_emac_clk.common,
+ &ahb_ts_clk.common,
+ &ahb_spi0_clk.common,
+ &ahb_spi1_clk.common,
+ &ahb_spi2_clk.common,
+ &ahb_gps_clk.common,
+ &ahb_hstimer_clk.common,
+ &ahb_ve_clk.common,
+ &ahb_tve_clk.common,
+ &ahb_lcd_clk.common,
+ &ahb_csi_clk.common,
+ &ahb_hdmi_clk.common,
+ &ahb_de_be_clk.common,
+ &ahb_de_fe_clk.common,
+ &ahb_iep_clk.common,
+ &ahb_gpu_clk.common,
+ &apb0_codec_clk.common,
+ &apb0_spdif_clk.common,
+ &apb0_i2s_clk.common,
+ &apb0_pio_clk.common,
+ &apb0_ir_clk.common,
+ &apb0_keypad_clk.common,
+ &apb1_i2c0_clk.common,
+ &apb1_i2c1_clk.common,
+ &apb1_i2c2_clk.common,
+ &apb1_uart0_clk.common,
+ &apb1_uart1_clk.common,
+ &apb1_uart2_clk.common,
+ &apb1_uart3_clk.common,
+ &nand_clk.common,
+ &mmc0_clk.common,
+ &mmc1_clk.common,
+ &mmc2_clk.common,
+ &ts_clk.common,
+ &ss_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &spi2_clk.common,
+ &ir_clk.common,
+ &i2s_clk.common,
+ &spdif_clk.common,
+ &keypad_clk.common,
+ &usb_ohci_clk.common,
+ &usb_phy0_clk.common,
+ &usb_phy1_clk.common,
+ &gps_clk.common,
+ &dram_ve_clk.common,
+ &dram_csi_clk.common,
+ &dram_ts_clk.common,
+ &dram_tve_clk.common,
+ &dram_de_fe_clk.common,
+ &dram_de_be_clk.common,
+ &dram_ace_clk.common,
+ &dram_iep_clk.common,
+ &de_be_clk.common,
+ &de_fe_clk.common,
+ &tcon_ch0_clk.common,
+ &tcon_ch1_sclk2_clk.common,
+ &tcon_ch1_sclk1_clk.common,
+ &csi_clk.common,
+ &ve_clk.common,
+ &codec_clk.common,
+ &avs_clk.common,
+ &hdmi_clk.common,
+ &gpu_clk.common,
+ &mbus_clk.common,
+ &iep_clk.common,
+};
+
+/* We hardcode the divider to 4 for now */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+ "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+ "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+ "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
+ "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x",
+ "pll-video0", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x",
+ "pll-video1", 1, 2, CLK_SET_RATE_PARENT);
+
+static struct clk_hw_onecell_data sun5i_a10s_hw_clks = {
+ .hws = {
+ [CLK_HOSC] = &hosc_clk.common.hw,
+ [CLK_PLL_CORE] = &pll_core_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw,
+ [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw,
+ [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw,
+ [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw,
+ [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw,
+ [CLK_CPU] = &cpu_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB] = &ahb_clk.common.hw,
+ [CLK_APB0] = &apb0_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_DRAM_AXI] = &axi_dram_clk.common.hw,
+ [CLK_AHB_OTG] = &ahb_otg_clk.common.hw,
+ [CLK_AHB_EHCI] = &ahb_ehci_clk.common.hw,
+ [CLK_AHB_OHCI] = &ahb_ohci_clk.common.hw,
+ [CLK_AHB_SS] = &ahb_ss_clk.common.hw,
+ [CLK_AHB_DMA] = &ahb_dma_clk.common.hw,
+ [CLK_AHB_BIST] = &ahb_bist_clk.common.hw,
+ [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw,
+ [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw,
+ [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw,
+ [CLK_AHB_NAND] = &ahb_nand_clk.common.hw,
+ [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw,
+ [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw,
+ [CLK_AHB_TS] = &ahb_ts_clk.common.hw,
+ [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw,
+ [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw,
+ [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw,
+ [CLK_AHB_GPS] = &ahb_gps_clk.common.hw,
+ [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw,
+ [CLK_AHB_VE] = &ahb_ve_clk.common.hw,
+ [CLK_AHB_TVE] = &ahb_tve_clk.common.hw,
+ [CLK_AHB_LCD] = &ahb_lcd_clk.common.hw,
+ [CLK_AHB_CSI] = &ahb_csi_clk.common.hw,
+ [CLK_AHB_HDMI] = &ahb_hdmi_clk.common.hw,
+ [CLK_AHB_DE_BE] = &ahb_de_be_clk.common.hw,
+ [CLK_AHB_DE_FE] = &ahb_de_fe_clk.common.hw,
+ [CLK_AHB_IEP] = &ahb_iep_clk.common.hw,
+ [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw,
+ [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw,
+ [CLK_APB0_I2S] = &apb0_i2s_clk.common.hw,
+ [CLK_APB0_PIO] = &apb0_pio_clk.common.hw,
+ [CLK_APB0_IR] = &apb0_ir_clk.common.hw,
+ [CLK_APB0_KEYPAD] = &apb0_keypad_clk.common.hw,
+ [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw,
+ [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw,
+ [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw,
+ [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw,
+ [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw,
+ [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw,
+ [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw,
+ [CLK_NAND] = &nand_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_TS] = &ts_clk.common.hw,
+ [CLK_SS] = &ss_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_SPI2] = &spi2_clk.common.hw,
+ [CLK_IR] = &ir_clk.common.hw,
+ [CLK_I2S] = &i2s_clk.common.hw,
+ [CLK_KEYPAD] = &keypad_clk.common.hw,
+ [CLK_USB_OHCI] = &usb_ohci_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_GPS] = &gps_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_TS] = &dram_ts_clk.common.hw,
+ [CLK_DRAM_TVE] = &dram_tve_clk.common.hw,
+ [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw,
+ [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw,
+ [CLK_DRAM_ACE] = &dram_ace_clk.common.hw,
+ [CLK_DRAM_IEP] = &dram_iep_clk.common.hw,
+ [CLK_DE_BE] = &de_be_clk.common.hw,
+ [CLK_DE_FE] = &de_fe_clk.common.hw,
+ [CLK_TCON_CH0] = &tcon_ch0_clk.common.hw,
+ [CLK_TCON_CH1_SCLK] = &tcon_ch1_sclk2_clk.common.hw,
+ [CLK_TCON_CH1] = &tcon_ch1_sclk1_clk.common.hw,
+ [CLK_CSI] = &csi_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_CODEC] = &codec_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_HDMI] = &hdmi_clk.common.hw,
+ [CLK_GPU] = &gpu_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_IEP] = &iep_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun5i_a10s_ccu_resets[] = {
+ [RST_USB_PHY0] = { 0x0cc, BIT(0) },
+ [RST_USB_PHY1] = { 0x0cc, BIT(1) },
+
+ [RST_GPS] = { 0x0d0, BIT(30) },
+
+ [RST_DE_BE] = { 0x104, BIT(30) },
+
+ [RST_DE_FE] = { 0x10c, BIT(30) },
+
+ [RST_TVE] = { 0x118, BIT(29) },
+ [RST_LCD] = { 0x118, BIT(30) },
+
+ [RST_CSI] = { 0x134, BIT(30) },
+
+ [RST_VE] = { 0x13c, BIT(0) },
+
+ [RST_GPU] = { 0x154, BIT(30) },
+
+ [RST_IEP] = { 0x160, BIT(30) },
+};
+
+static const struct sunxi_ccu_desc sun5i_a10s_ccu_desc = {
+ .ccu_clks = sun5i_a10s_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun5i_a10s_ccu_clks),
+
+ .hw_clks = &sun5i_a10s_hw_clks,
+
+ .resets = sun5i_a10s_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun5i_a10s_ccu_resets),
+};
+
+/*
+ * The A13 is the A10s minus the TS, GPS, HDMI, I2S and the keypad
+ */
+static struct clk_hw_onecell_data sun5i_a13_hw_clks = {
+ .hws = {
+ [CLK_HOSC] = &hosc_clk.common.hw,
+ [CLK_PLL_CORE] = &pll_core_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw,
+ [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw,
+ [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw,
+ [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw,
+ [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw,
+ [CLK_CPU] = &cpu_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB] = &ahb_clk.common.hw,
+ [CLK_APB0] = &apb0_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_DRAM_AXI] = &axi_dram_clk.common.hw,
+ [CLK_AHB_OTG] = &ahb_otg_clk.common.hw,
+ [CLK_AHB_EHCI] = &ahb_ehci_clk.common.hw,
+ [CLK_AHB_OHCI] = &ahb_ohci_clk.common.hw,
+ [CLK_AHB_SS] = &ahb_ss_clk.common.hw,
+ [CLK_AHB_DMA] = &ahb_dma_clk.common.hw,
+ [CLK_AHB_BIST] = &ahb_bist_clk.common.hw,
+ [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw,
+ [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw,
+ [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw,
+ [CLK_AHB_NAND] = &ahb_nand_clk.common.hw,
+ [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw,
+ [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw,
+ [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw,
+ [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw,
+ [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw,
+ [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw,
+ [CLK_AHB_VE] = &ahb_ve_clk.common.hw,
+ [CLK_AHB_TVE] = &ahb_tve_clk.common.hw,
+ [CLK_AHB_LCD] = &ahb_lcd_clk.common.hw,
+ [CLK_AHB_CSI] = &ahb_csi_clk.common.hw,
+ [CLK_AHB_DE_BE] = &ahb_de_be_clk.common.hw,
+ [CLK_AHB_DE_FE] = &ahb_de_fe_clk.common.hw,
+ [CLK_AHB_IEP] = &ahb_iep_clk.common.hw,
+ [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw,
+ [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw,
+ [CLK_APB0_PIO] = &apb0_pio_clk.common.hw,
+ [CLK_APB0_IR] = &apb0_ir_clk.common.hw,
+ [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw,
+ [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw,
+ [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw,
+ [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw,
+ [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw,
+ [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw,
+ [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw,
+ [CLK_NAND] = &nand_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_SS] = &ss_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_SPI2] = &spi2_clk.common.hw,
+ [CLK_IR] = &ir_clk.common.hw,
+ [CLK_USB_OHCI] = &usb_ohci_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_TVE] = &dram_tve_clk.common.hw,
+ [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw,
+ [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw,
+ [CLK_DRAM_ACE] = &dram_ace_clk.common.hw,
+ [CLK_DRAM_IEP] = &dram_iep_clk.common.hw,
+ [CLK_DE_BE] = &de_be_clk.common.hw,
+ [CLK_DE_FE] = &de_fe_clk.common.hw,
+ [CLK_TCON_CH0] = &tcon_ch0_clk.common.hw,
+ [CLK_TCON_CH1_SCLK] = &tcon_ch1_sclk2_clk.common.hw,
+ [CLK_TCON_CH1] = &tcon_ch1_sclk1_clk.common.hw,
+ [CLK_CSI] = &csi_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_CODEC] = &codec_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_GPU] = &gpu_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_IEP] = &iep_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static const struct sunxi_ccu_desc sun5i_a13_ccu_desc = {
+ .ccu_clks = sun5i_a10s_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun5i_a10s_ccu_clks),
+
+ .hw_clks = &sun5i_a13_hw_clks,
+
+ .resets = sun5i_a10s_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun5i_a10s_ccu_resets),
+};
+
+/*
+ * The GR8 is the A10s CCU minus the HDMI and keypad, plus SPDIF
+ */
+static struct clk_hw_onecell_data sun5i_gr8_hw_clks = {
+ .hws = {
+ [CLK_HOSC] = &hosc_clk.common.hw,
+ [CLK_PLL_CORE] = &pll_core_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw,
+ [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw,
+ [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw,
+ [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw,
+ [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw,
+ [CLK_CPU] = &cpu_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB] = &ahb_clk.common.hw,
+ [CLK_APB0] = &apb0_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_DRAM_AXI] = &axi_dram_clk.common.hw,
+ [CLK_AHB_OTG] = &ahb_otg_clk.common.hw,
+ [CLK_AHB_EHCI] = &ahb_ehci_clk.common.hw,
+ [CLK_AHB_OHCI] = &ahb_ohci_clk.common.hw,
+ [CLK_AHB_SS] = &ahb_ss_clk.common.hw,
+ [CLK_AHB_DMA] = &ahb_dma_clk.common.hw,
+ [CLK_AHB_BIST] = &ahb_bist_clk.common.hw,
+ [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw,
+ [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw,
+ [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw,
+ [CLK_AHB_NAND] = &ahb_nand_clk.common.hw,
+ [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw,
+ [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw,
+ [CLK_AHB_TS] = &ahb_ts_clk.common.hw,
+ [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw,
+ [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw,
+ [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw,
+ [CLK_AHB_GPS] = &ahb_gps_clk.common.hw,
+ [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw,
+ [CLK_AHB_VE] = &ahb_ve_clk.common.hw,
+ [CLK_AHB_TVE] = &ahb_tve_clk.common.hw,
+ [CLK_AHB_LCD] = &ahb_lcd_clk.common.hw,
+ [CLK_AHB_CSI] = &ahb_csi_clk.common.hw,
+ [CLK_AHB_DE_BE] = &ahb_de_be_clk.common.hw,
+ [CLK_AHB_DE_FE] = &ahb_de_fe_clk.common.hw,
+ [CLK_AHB_IEP] = &ahb_iep_clk.common.hw,
+ [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw,
+ [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw,
+ [CLK_APB0_SPDIF] = &apb0_spdif_clk.common.hw,
+ [CLK_APB0_I2S] = &apb0_i2s_clk.common.hw,
+ [CLK_APB0_PIO] = &apb0_pio_clk.common.hw,
+ [CLK_APB0_IR] = &apb0_ir_clk.common.hw,
+ [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw,
+ [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw,
+ [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw,
+ [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw,
+ [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw,
+ [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw,
+ [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw,
+ [CLK_NAND] = &nand_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_TS] = &ts_clk.common.hw,
+ [CLK_SS] = &ss_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_SPI2] = &spi2_clk.common.hw,
+ [CLK_IR] = &ir_clk.common.hw,
+ [CLK_I2S] = &i2s_clk.common.hw,
+ [CLK_SPDIF] = &spdif_clk.common.hw,
+ [CLK_USB_OHCI] = &usb_ohci_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_GPS] = &gps_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_TS] = &dram_ts_clk.common.hw,
+ [CLK_DRAM_TVE] = &dram_tve_clk.common.hw,
+ [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw,
+ [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw,
+ [CLK_DRAM_ACE] = &dram_ace_clk.common.hw,
+ [CLK_DRAM_IEP] = &dram_iep_clk.common.hw,
+ [CLK_DE_BE] = &de_be_clk.common.hw,
+ [CLK_DE_FE] = &de_fe_clk.common.hw,
+ [CLK_TCON_CH0] = &tcon_ch0_clk.common.hw,
+ [CLK_TCON_CH1_SCLK] = &tcon_ch1_sclk2_clk.common.hw,
+ [CLK_TCON_CH1] = &tcon_ch1_sclk1_clk.common.hw,
+ [CLK_CSI] = &csi_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_CODEC] = &codec_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_GPU] = &gpu_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_IEP] = &iep_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static const struct sunxi_ccu_desc sun5i_gr8_ccu_desc = {
+ .ccu_clks = sun5i_a10s_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun5i_a10s_ccu_clks),
+
+ .hw_clks = &sun5i_gr8_hw_clks,
+
+ .resets = sun5i_a10s_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun5i_a10s_ccu_resets),
+};
+
+static void __init sun5i_ccu_init(struct device_node *node,
+ const struct sunxi_ccu_desc *desc)
+{
+ void __iomem *reg;
+ u32 val;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n",
+ of_node_full_name(node));
+ return;
+ }
+
+ /* Force the PLL-Audio-1x divider to 4 */
+ val = readl(reg + SUN5I_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUN5I_PLL_AUDIO_REG);
+
+ /*
+ * Use the peripheral PLL as the AHB parent, instead of CPU /
+ * AXI which have rate changes due to cpufreq.
+ *
+ * This is especially a big deal for the HS timer whose parent
+ * clock is AHB.
+ */
+ val = readl(reg + SUN5I_AHB_REG);
+ val &= ~GENMASK(7, 6);
+ writel(val | (2 << 6), reg + SUN5I_AHB_REG);
+
+ sunxi_ccu_probe(node, reg, desc);
+}
+
+static void __init sun5i_a10s_ccu_setup(struct device_node *node)
+{
+ sun5i_ccu_init(node, &sun5i_a10s_ccu_desc);
+}
+CLK_OF_DECLARE(sun5i_a10s_ccu, "allwinner,sun5i-a10s-ccu",
+ sun5i_a10s_ccu_setup);
+
+static void __init sun5i_a13_ccu_setup(struct device_node *node)
+{
+ sun5i_ccu_init(node, &sun5i_a13_ccu_desc);
+}
+CLK_OF_DECLARE(sun5i_a13_ccu, "allwinner,sun5i-a13-ccu",
+ sun5i_a13_ccu_setup);
+
+static void __init sun5i_gr8_ccu_setup(struct device_node *node)
+{
+ sun5i_ccu_init(node, &sun5i_gr8_ccu_desc);
+}
+CLK_OF_DECLARE(sun5i_gr8_ccu, "nextthing,gr8-ccu",
+ sun5i_gr8_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.h b/drivers/clk/sunxi-ng/ccu-sun5i.h
new file mode 100644
index 000000000000..8144487eb7ca
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun5i.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN5I_H_
+#define _CCU_SUN5I_H_
+
+#include <dt-bindings/clock/sun5i-ccu.h>
+#include <dt-bindings/reset/sun5i-ccu.h>
+
+/* The HOSC is exported */
+#define CLK_PLL_CORE 2
+#define CLK_PLL_AUDIO_BASE 3
+#define CLK_PLL_AUDIO 4
+#define CLK_PLL_AUDIO_2X 5
+#define CLK_PLL_AUDIO_4X 6
+#define CLK_PLL_AUDIO_8X 7
+#define CLK_PLL_VIDEO0 8
+#define CLK_PLL_VIDEO0_2X 9
+#define CLK_PLL_VE 10
+#define CLK_PLL_DDR_BASE 11
+#define CLK_PLL_DDR 12
+#define CLK_PLL_DDR_OTHER 13
+#define CLK_PLL_PERIPH 14
+#define CLK_PLL_VIDEO1 15
+#define CLK_PLL_VIDEO1_2X 16
+
+/* The CPU clock is exported */
+
+#define CLK_AXI 18
+#define CLK_AHB 19
+#define CLK_APB0 20
+#define CLK_APB1 21
+#define CLK_DRAM_AXI 22
+
+/* AHB gates are exported */
+/* APB0 gates are exported */
+/* APB1 gates are exported */
+/* Modules clocks are exported */
+/* USB clocks are exported */
+/* GPS clock is exported */
+/* DRAM gates are exported */
+/* More display modules clocks are exported */
+
+#define CLK_TCON_CH1_SCLK 91
+
+/* The rest of the module clocks are exported */
+
+#define CLK_MBUS 99
+
+/* And finally the IEP clock */
+
+#define CLK_NUMBER (CLK_IEP + 1)
+
+#endif /* _CCU_SUN5I_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
index fc75a335a7ce..4c9a920ff4ab 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
@@ -468,8 +468,8 @@ static SUNXI_CCU_MUX_WITH_GATE(daudio0_clk, "daudio0", daudio_parents,
static SUNXI_CCU_MUX_WITH_GATE(daudio1_clk, "daudio1", daudio_parents,
0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
-static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio",
- 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", daudio_parents,
+ 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
0x0cc, BIT(8), 0);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
index 9bd1f78a0547..a7b3c08ed0e2 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
@@ -170,7 +170,7 @@ static SUNXI_CCU_N_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1",
static const char * const cpux_parents[] = { "osc32k", "osc24M",
"pll-cpux" , "pll-cpux" };
static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
- 0x050, 16, 2, CLK_IS_CRITICAL);
+ 0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT);
static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0);
@@ -440,7 +440,7 @@ static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio",
- 0x140, BIT(31), 0);
+ 0x140, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(ac_dig_4x_clk, "ac-dig-4x", "pll-audio-4x",
0x140, BIT(30), 0);
static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
@@ -468,7 +468,7 @@ static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(drc_clk, "drc",
0x180, 0, 4, 24, 3, BIT(31), 0);
static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
- 0x1a0, 0, 3, BIT(31), 0);
+ 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT);
static const char * const ats_parents[] = { "osc24M", "pll-periph" };
static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", ats_parents,
@@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = {
.num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets),
};
+static struct ccu_mux_nb sun8i_a33_cpu_nb = {
+ .common = &cpux_clk.common,
+ .cm = &cpux_clk.mux,
+ .delay_us = 1, /* > 8 clock cycles at 24 MHz */
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+};
+
static void __init sun8i_a33_ccu_setup(struct device_node *node)
{
void __iomem *reg;
@@ -775,6 +782,9 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
+
+ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+ &sun8i_a33_cpu_nb);
}
CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu",
sun8i_a33_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 21c427d86f28..a26c8a19fe93 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -803,6 +803,13 @@ static const struct sunxi_ccu_desc sun8i_h3_ccu_desc = {
.num_resets = ARRAY_SIZE(sun8i_h3_ccu_resets),
};
+static struct ccu_mux_nb sun8i_h3_cpu_nb = {
+ .common = &cpux_clk.common,
+ .cm = &cpux_clk.mux,
+ .delay_us = 1, /* > 8 clock cycles at 24 MHz */
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+};
+
static void __init sun8i_h3_ccu_setup(struct device_node *node)
{
void __iomem *reg;
@@ -821,6 +828,9 @@ static void __init sun8i_h3_ccu_setup(struct device_node *node)
writel(val | (3 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
sunxi_ccu_probe(node, reg, &sun8i_h3_ccu_desc);
+
+ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+ &sun8i_h3_cpu_nb);
}
CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu",
sun8i_h3_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
new file mode 100644
index 000000000000..e58706b40ae9
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * Based on ccu-sun8i-h3.c, which is:
+ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun8i-v3s.h"
+
+static SUNXI_CCU_NKMP_WITH_GATE_LOCK(pll_cpu_clk, "pll-cpu",
+ "osc24M", 0x000,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ 0, 2, /* M */
+ 16, 2, /* P */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+/*
+ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
+ * the base (2x, 4x and 8x), and one variable divider (the one true
+ * pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names
+ */
+#define SUN8I_V3S_PLL_AUDIO_REG 0x008
+
+static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
+ "osc24M", 0x008,
+ 8, 7, /* N */
+ 0, 5, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video",
+ "osc24M", 0x0010,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
+ "osc24M", 0x0018,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr",
+ "osc24M", 0x020,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ 0, 2, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph0_clk, "pll-periph0",
+ "osc24M", 0x028,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 2, /* post-div */
+ 0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_isp_clk, "pll-isp",
+ "osc24M", 0x002c,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1",
+ "osc24M", 0x044,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 2, /* post-div */
+ 0);
+
+static const char * const cpu_parents[] = { "osc32k", "osc24M",
+ "pll-cpu", "pll-cpu" };
+static SUNXI_CCU_MUX(cpu_clk, "cpu", cpu_parents,
+ 0x050, 16, 2, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x050, 0, 2, 0);
+
+static const char * const ahb1_parents[] = { "osc32k", "osc24M",
+ "axi", "pll-periph0" };
+static struct ccu_div ahb1_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .mux = {
+ .shift = 12,
+ .width = 2,
+
+ .variable_prediv = {
+ .index = 3,
+ .shift = 6,
+ .width = 2,
+ },
+ },
+
+ .common = {
+ .reg = 0x054,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb1",
+ ahb1_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct clk_div_table apb1_div_table[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { /* Sentinel */ },
+};
+static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1",
+ 0x054, 8, 2, apb1_div_table, 0);
+
+static const char * const apb2_parents[] = { "osc32k", "osc24M",
+ "pll-periph0", "pll-periph0" };
+static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static const char * const ahb2_parents[] = { "ahb1", "pll-periph0" };
+static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = {
+ { .index = 1, .div = 2 },
+};
+static struct ccu_mux ahb2_clk = {
+ .mux = {
+ .shift = 0,
+ .width = 1,
+ .fixed_predivs = ahb2_fixed_predivs,
+ .n_predivs = ARRAY_SIZE(ahb2_fixed_predivs),
+ },
+
+ .common = {
+ .reg = 0x05c,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb2",
+ ahb2_parents,
+ &ccu_mux_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb1",
+ 0x060, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1",
+ 0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1",
+ 0x060, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1",
+ 0x060, BIT(9), 0);
+static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1",
+ 0x060, BIT(10), 0);
+static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1",
+ 0x060, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb2",
+ 0x060, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1",
+ 0x060, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1",
+ 0x060, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1",
+ 0x060, BIT(24), 0);
+static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb1",
+ 0x060, BIT(26), 0);
+static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb1",
+ 0x060, BIT(29), 0);
+
+static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1",
+ 0x064, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_tcon0_clk, "bus-tcon0", "ahb1",
+ 0x064, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1",
+ 0x064, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb1",
+ 0x064, BIT(12), 0);
+
+static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1",
+ 0x068, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1",
+ 0x068, BIT(5), 0);
+
+static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2",
+ 0x06c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2",
+ 0x06c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2",
+ 0x06c, BIT(16), 0);
+static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2",
+ 0x06c, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2",
+ 0x06c, BIT(18), 0);
+
+static SUNXI_CCU_GATE(bus_ephy_clk, "bus-ephy", "ahb1",
+ 0x070, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb1",
+ 0x070, BIT(7), 0);
+
+static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0",
+ "pll-periph1" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0",
+ 0x088, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0",
+ 0x088, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1",
+ 0x08c, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1",
+ 0x08c, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2",
+ 0x090, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2",
+ 0x090, 8, 3, 0);
+
+static const char * const ce_parents[] = { "osc24M", "pll-periph0", };
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x09c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
+ 0x0cc, BIT(8), 0);
+static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M",
+ 0x0cc, BIT(16), 0);
+
+static const char * const dram_parents[] = { "pll-ddr", "pll-periph0-2x" };
+static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents,
+ 0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram",
+ 0x100, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "dram",
+ 0x100, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_ehci_clk, "dram-ehci", "dram",
+ 0x100, BIT(17), 0);
+static SUNXI_CCU_GATE(dram_ohci_clk, "dram-ohci", "dram",
+ 0x100, BIT(18), 0);
+
+static const char * const de_parents[] = { "pll-video", "pll-periph0" };
+static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents,
+ 0x104, 0, 4, 24, 2, BIT(31), 0);
+
+static const char * const tcon_parents[] = { "pll-video" };
+static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents,
+ 0x118, 0, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M",
+ 0x130, BIT(31), 0);
+
+static const char * const csi_mclk_parents[] = { "osc24M", "pll-video",
+ "pll-periph0", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(csi0_mclk_clk, "csi0-mclk", csi_mclk_parents,
+ 0x130, 0, 5, 8, 3, BIT(15), 0);
+
+static const char * const csi1_sclk_parents[] = { "pll-video", "pll-isp" };
+static SUNXI_CCU_M_WITH_MUX_GATE(csi1_sclk_clk, "csi-sclk", csi1_sclk_parents,
+ 0x134, 16, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(csi1_mclk_clk, "csi-mclk", csi_mclk_parents,
+ 0x134, 0, 5, 8, 3, BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
+ 0x13c, 16, 3, BIT(31), 0);
+
+static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio",
+ 0x140, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
+ 0x144, BIT(31), 0);
+
+static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x",
+ "pll-ddr" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
+ 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL);
+
+static const char * const mipi_csi_parents[] = { "pll-video", "pll-periph0",
+ "pll-isp" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mipi_csi_clk, "mipi-csi", mipi_csi_parents,
+ 0x16c, 0, 3, 24, 2, BIT(31), 0);
+
+static struct ccu_common *sun8i_v3s_ccu_clks[] = {
+ &pll_cpu_clk.common,
+ &pll_audio_base_clk.common,
+ &pll_video_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr_clk.common,
+ &pll_periph0_clk.common,
+ &pll_isp_clk.common,
+ &pll_periph1_clk.common,
+ &cpu_clk.common,
+ &axi_clk.common,
+ &ahb1_clk.common,
+ &apb1_clk.common,
+ &apb2_clk.common,
+ &ahb2_clk.common,
+ &bus_ce_clk.common,
+ &bus_dma_clk.common,
+ &bus_mmc0_clk.common,
+ &bus_mmc1_clk.common,
+ &bus_mmc2_clk.common,
+ &bus_dram_clk.common,
+ &bus_emac_clk.common,
+ &bus_hstimer_clk.common,
+ &bus_spi0_clk.common,
+ &bus_otg_clk.common,
+ &bus_ehci0_clk.common,
+ &bus_ohci0_clk.common,
+ &bus_ve_clk.common,
+ &bus_tcon0_clk.common,
+ &bus_csi_clk.common,
+ &bus_de_clk.common,
+ &bus_codec_clk.common,
+ &bus_pio_clk.common,
+ &bus_i2c0_clk.common,
+ &bus_i2c1_clk.common,
+ &bus_uart0_clk.common,
+ &bus_uart1_clk.common,
+ &bus_uart2_clk.common,
+ &bus_ephy_clk.common,
+ &bus_dbg_clk.common,
+ &mmc0_clk.common,
+ &mmc0_sample_clk.common,
+ &mmc0_output_clk.common,
+ &mmc1_clk.common,
+ &mmc1_sample_clk.common,
+ &mmc1_output_clk.common,
+ &mmc2_clk.common,
+ &mmc2_sample_clk.common,
+ &mmc2_output_clk.common,
+ &ce_clk.common,
+ &spi0_clk.common,
+ &usb_phy0_clk.common,
+ &usb_ohci0_clk.common,
+ &dram_clk.common,
+ &dram_ve_clk.common,
+ &dram_csi_clk.common,
+ &dram_ohci_clk.common,
+ &dram_ehci_clk.common,
+ &de_clk.common,
+ &tcon_clk.common,
+ &csi_misc_clk.common,
+ &csi0_mclk_clk.common,
+ &csi1_sclk_clk.common,
+ &csi1_mclk_clk.common,
+ &ve_clk.common,
+ &ac_dig_clk.common,
+ &avs_clk.common,
+ &mbus_clk.common,
+ &mipi_csi_clk.common,
+};
+
+/* We hardcode the divider to 4 for now */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+ "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+ "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+ "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
+ "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x",
+ "pll-periph0", 1, 2, 0);
+
+static struct clk_hw_onecell_data sun8i_v3s_hw_clks = {
+ .hws = {
+ [CLK_PLL_CPU] = &pll_cpu_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO] = &pll_video_clk.common.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw,
+ [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw,
+ [CLK_PLL_ISP] = &pll_isp_clk.common.hw,
+ [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw,
+ [CLK_CPU] = &cpu_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB1] = &ahb1_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_APB2] = &apb2_clk.common.hw,
+ [CLK_AHB2] = &ahb2_clk.common.hw,
+ [CLK_BUS_CE] = &bus_ce_clk.common.hw,
+ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
+ [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw,
+ [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw,
+ [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw,
+ [CLK_BUS_DRAM] = &bus_dram_clk.common.hw,
+ [CLK_BUS_EMAC] = &bus_emac_clk.common.hw,
+ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
+ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
+ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
+ [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw,
+ [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw,
+ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
+ [CLK_BUS_TCON0] = &bus_tcon0_clk.common.hw,
+ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
+ [CLK_BUS_DE] = &bus_de_clk.common.hw,
+ [CLK_BUS_CODEC] = &bus_codec_clk.common.hw,
+ [CLK_BUS_PIO] = &bus_pio_clk.common.hw,
+ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
+ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
+ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
+ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
+ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
+ [CLK_BUS_EPHY] = &bus_ephy_clk.common.hw,
+ [CLK_BUS_DBG] = &bus_dbg_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw,
+ [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_CE] = &ce_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw,
+ [CLK_DRAM] = &dram_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_EHCI] = &dram_ehci_clk.common.hw,
+ [CLK_DRAM_OHCI] = &dram_ohci_clk.common.hw,
+ [CLK_DE] = &de_clk.common.hw,
+ [CLK_TCON0] = &tcon_clk.common.hw,
+ [CLK_CSI_MISC] = &csi_misc_clk.common.hw,
+ [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw,
+ [CLK_CSI1_SCLK] = &csi1_sclk_clk.common.hw,
+ [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_AC_DIG] = &ac_dig_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_MIPI_CSI] = &mipi_csi_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_v3s_ccu_resets[] = {
+ [RST_USB_PHY0] = { 0x0cc, BIT(0) },
+
+ [RST_MBUS] = { 0x0fc, BIT(31) },
+
+ [RST_BUS_CE] = { 0x2c0, BIT(5) },
+ [RST_BUS_DMA] = { 0x2c0, BIT(6) },
+ [RST_BUS_MMC0] = { 0x2c0, BIT(8) },
+ [RST_BUS_MMC1] = { 0x2c0, BIT(9) },
+ [RST_BUS_MMC2] = { 0x2c0, BIT(10) },
+ [RST_BUS_DRAM] = { 0x2c0, BIT(14) },
+ [RST_BUS_EMAC] = { 0x2c0, BIT(17) },
+ [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) },
+ [RST_BUS_SPI0] = { 0x2c0, BIT(20) },
+ [RST_BUS_OTG] = { 0x2c0, BIT(23) },
+ [RST_BUS_EHCI0] = { 0x2c0, BIT(26) },
+ [RST_BUS_OHCI0] = { 0x2c0, BIT(29) },
+
+ [RST_BUS_VE] = { 0x2c4, BIT(0) },
+ [RST_BUS_TCON0] = { 0x2c4, BIT(3) },
+ [RST_BUS_CSI] = { 0x2c4, BIT(8) },
+ [RST_BUS_DE] = { 0x2c4, BIT(12) },
+ [RST_BUS_DBG] = { 0x2c4, BIT(31) },
+
+ [RST_BUS_EPHY] = { 0x2c8, BIT(2) },
+
+ [RST_BUS_CODEC] = { 0x2d0, BIT(0) },
+
+ [RST_BUS_I2C0] = { 0x2d8, BIT(0) },
+ [RST_BUS_I2C1] = { 0x2d8, BIT(1) },
+ [RST_BUS_UART0] = { 0x2d8, BIT(16) },
+ [RST_BUS_UART1] = { 0x2d8, BIT(17) },
+ [RST_BUS_UART2] = { 0x2d8, BIT(18) },
+};
+
+static const struct sunxi_ccu_desc sun8i_v3s_ccu_desc = {
+ .ccu_clks = sun8i_v3s_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_ccu_clks),
+
+ .hw_clks = &sun8i_v3s_hw_clks,
+
+ .resets = sun8i_v3s_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun8i_v3s_ccu_resets),
+};
+
+static void __init sun8i_v3s_ccu_setup(struct device_node *node)
+{
+ void __iomem *reg;
+ u32 val;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n",
+ of_node_full_name(node));
+ return;
+ }
+
+ /* Force the PLL-Audio-1x divider to 4 */
+ val = readl(reg + SUN8I_V3S_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUN8I_V3S_PLL_AUDIO_REG);
+
+ sunxi_ccu_probe(node, reg, &sun8i_v3s_ccu_desc);
+}
+CLK_OF_DECLARE(sun8i_v3s_ccu, "allwinner,sun8i-v3s-ccu",
+ sun8i_v3s_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
new file mode 100644
index 000000000000..4a4d36fdad96
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Icenowy Zheng <icenowy@aosc.xyz>
+ *
+ * Based on ccu-sun8i-h3.h, which is:
+ * Copyright (c) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN8I_H3_H_
+#define _CCU_SUN8I_H3_H_
+
+#include <dt-bindings/clock/sun8i-v3s-ccu.h>
+#include <dt-bindings/reset/sun8i-v3s-ccu.h>
+
+#define CLK_PLL_CPU 0
+#define CLK_PLL_AUDIO_BASE 1
+#define CLK_PLL_AUDIO 2
+#define CLK_PLL_AUDIO_2X 3
+#define CLK_PLL_AUDIO_4X 4
+#define CLK_PLL_AUDIO_8X 5
+#define CLK_PLL_VIDEO 6
+#define CLK_PLL_VE 7
+#define CLK_PLL_DDR 8
+#define CLK_PLL_PERIPH0 9
+#define CLK_PLL_PERIPH0_2X 10
+#define CLK_PLL_ISP 11
+#define CLK_PLL_PERIPH1 12
+/* Reserve one number for not implemented and not used PLL_DDR1 */
+
+/* The CPU clock is exported */
+
+#define CLK_AXI 15
+#define CLK_AHB1 16
+#define CLK_APB1 17
+#define CLK_APB2 18
+#define CLK_AHB2 19
+
+/* All the bus gates are exported */
+
+/* The first bunch of module clocks are exported */
+
+#define CLK_DRAM 58
+
+/* All the DRAM gates are exported */
+
+/* Some more module clocks are exported */
+
+#define CLK_MBUS 72
+
+/* And the GPU module clock is exported */
+
+#define CLK_NUMBER (CLK_MIPI_CSI + 1)
+
+#endif /* _CCU_SUN8I_H3_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
new file mode 100644
index 000000000000..3fc27db0a49a
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "ccu_common.h"
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_reset.h"
+
+#include "ccu-sun9i-a80-de.h"
+
+static SUNXI_CCU_GATE(fe0_clk, "fe0", "fe0-div",
+ 0x00, BIT(0), 0);
+static SUNXI_CCU_GATE(fe1_clk, "fe1", "fe1-div",
+ 0x00, BIT(1), 0);
+static SUNXI_CCU_GATE(fe2_clk, "fe2", "fe2-div",
+ 0x00, BIT(2), 0);
+static SUNXI_CCU_GATE(iep_deu0_clk, "iep-deu0", "de",
+ 0x00, BIT(4), 0);
+static SUNXI_CCU_GATE(iep_deu1_clk, "iep-deu1", "de",
+ 0x00, BIT(5), 0);
+static SUNXI_CCU_GATE(be0_clk, "be0", "be0-div",
+ 0x00, BIT(8), 0);
+static SUNXI_CCU_GATE(be1_clk, "be1", "be1-div",
+ 0x00, BIT(9), 0);
+static SUNXI_CCU_GATE(be2_clk, "be2", "be2-div",
+ 0x00, BIT(10), 0);
+static SUNXI_CCU_GATE(iep_drc0_clk, "iep-drc0", "de",
+ 0x00, BIT(12), 0);
+static SUNXI_CCU_GATE(iep_drc1_clk, "iep-drc1", "de",
+ 0x00, BIT(13), 0);
+static SUNXI_CCU_GATE(merge_clk, "merge", "de",
+ 0x00, BIT(20), 0);
+
+static SUNXI_CCU_GATE(dram_fe0_clk, "dram-fe0", "sdram",
+ 0x04, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_fe1_clk, "dram-fe1", "sdram",
+ 0x04, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_fe2_clk, "dram-fe2", "sdram",
+ 0x04, BIT(2), 0);
+static SUNXI_CCU_GATE(dram_deu0_clk, "dram-deu0", "sdram",
+ 0x04, BIT(4), 0);
+static SUNXI_CCU_GATE(dram_deu1_clk, "dram-deu1", "sdram",
+ 0x04, BIT(5), 0);
+static SUNXI_CCU_GATE(dram_be0_clk, "dram-be0", "sdram",
+ 0x04, BIT(8), 0);
+static SUNXI_CCU_GATE(dram_be1_clk, "dram-be1", "sdram",
+ 0x04, BIT(9), 0);
+static SUNXI_CCU_GATE(dram_be2_clk, "dram-be2", "sdram",
+ 0x04, BIT(10), 0);
+static SUNXI_CCU_GATE(dram_drc0_clk, "dram-drc0", "sdram",
+ 0x04, BIT(12), 0);
+static SUNXI_CCU_GATE(dram_drc1_clk, "dram-drc1", "sdram",
+ 0x04, BIT(13), 0);
+
+static SUNXI_CCU_GATE(bus_fe0_clk, "bus-fe0", "bus-de",
+ 0x08, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_fe1_clk, "bus-fe1", "bus-de",
+ 0x08, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_fe2_clk, "bus-fe2", "bus-de",
+ 0x08, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_deu0_clk, "bus-deu0", "bus-de",
+ 0x08, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_deu1_clk, "bus-deu1", "bus-de",
+ 0x08, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_be0_clk, "bus-be0", "bus-de",
+ 0x08, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_be1_clk, "bus-be1", "bus-de",
+ 0x08, BIT(9), 0);
+static SUNXI_CCU_GATE(bus_be2_clk, "bus-be2", "bus-de",
+ 0x08, BIT(10), 0);
+static SUNXI_CCU_GATE(bus_drc0_clk, "bus-drc0", "bus-de",
+ 0x08, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_drc1_clk, "bus-drc1", "bus-de",
+ 0x08, BIT(13), 0);
+
+static SUNXI_CCU_M(fe0_div_clk, "fe0-div", "de", 0x20, 0, 4, 0);
+static SUNXI_CCU_M(fe1_div_clk, "fe1-div", "de", 0x20, 4, 4, 0);
+static SUNXI_CCU_M(fe2_div_clk, "fe2-div", "de", 0x20, 8, 4, 0);
+static SUNXI_CCU_M(be0_div_clk, "be0-div", "de", 0x20, 16, 4, 0);
+static SUNXI_CCU_M(be1_div_clk, "be1-div", "de", 0x20, 20, 4, 0);
+static SUNXI_CCU_M(be2_div_clk, "be2-div", "de", 0x20, 24, 4, 0);
+
+static struct ccu_common *sun9i_a80_de_clks[] = {
+ &fe0_clk.common,
+ &fe1_clk.common,
+ &fe2_clk.common,
+ &iep_deu0_clk.common,
+ &iep_deu1_clk.common,
+ &be0_clk.common,
+ &be1_clk.common,
+ &be2_clk.common,
+ &iep_drc0_clk.common,
+ &iep_drc1_clk.common,
+ &merge_clk.common,
+
+ &dram_fe0_clk.common,
+ &dram_fe1_clk.common,
+ &dram_fe2_clk.common,
+ &dram_deu0_clk.common,
+ &dram_deu1_clk.common,
+ &dram_be0_clk.common,
+ &dram_be1_clk.common,
+ &dram_be2_clk.common,
+ &dram_drc0_clk.common,
+ &dram_drc1_clk.common,
+
+ &bus_fe0_clk.common,
+ &bus_fe1_clk.common,
+ &bus_fe2_clk.common,
+ &bus_deu0_clk.common,
+ &bus_deu1_clk.common,
+ &bus_be0_clk.common,
+ &bus_be1_clk.common,
+ &bus_be2_clk.common,
+ &bus_drc0_clk.common,
+ &bus_drc1_clk.common,
+
+ &fe0_div_clk.common,
+ &fe1_div_clk.common,
+ &fe2_div_clk.common,
+ &be0_div_clk.common,
+ &be1_div_clk.common,
+ &be2_div_clk.common,
+};
+
+static struct clk_hw_onecell_data sun9i_a80_de_hw_clks = {
+ .hws = {
+ [CLK_FE0] = &fe0_clk.common.hw,
+ [CLK_FE1] = &fe1_clk.common.hw,
+ [CLK_FE2] = &fe2_clk.common.hw,
+ [CLK_IEP_DEU0] = &iep_deu0_clk.common.hw,
+ [CLK_IEP_DEU1] = &iep_deu1_clk.common.hw,
+ [CLK_BE0] = &be0_clk.common.hw,
+ [CLK_BE1] = &be1_clk.common.hw,
+ [CLK_BE2] = &be2_clk.common.hw,
+ [CLK_IEP_DRC0] = &iep_drc0_clk.common.hw,
+ [CLK_IEP_DRC1] = &iep_drc1_clk.common.hw,
+ [CLK_MERGE] = &merge_clk.common.hw,
+
+ [CLK_DRAM_FE0] = &dram_fe0_clk.common.hw,
+ [CLK_DRAM_FE1] = &dram_fe1_clk.common.hw,
+ [CLK_DRAM_FE2] = &dram_fe2_clk.common.hw,
+ [CLK_DRAM_DEU0] = &dram_deu0_clk.common.hw,
+ [CLK_DRAM_DEU1] = &dram_deu1_clk.common.hw,
+ [CLK_DRAM_BE0] = &dram_be0_clk.common.hw,
+ [CLK_DRAM_BE1] = &dram_be1_clk.common.hw,
+ [CLK_DRAM_BE2] = &dram_be2_clk.common.hw,
+ [CLK_DRAM_DRC0] = &dram_drc0_clk.common.hw,
+ [CLK_DRAM_DRC1] = &dram_drc1_clk.common.hw,
+
+ [CLK_BUS_FE0] = &bus_fe0_clk.common.hw,
+ [CLK_BUS_FE1] = &bus_fe1_clk.common.hw,
+ [CLK_BUS_FE2] = &bus_fe2_clk.common.hw,
+ [CLK_BUS_DEU0] = &bus_deu0_clk.common.hw,
+ [CLK_BUS_DEU1] = &bus_deu1_clk.common.hw,
+ [CLK_BUS_BE0] = &bus_be0_clk.common.hw,
+ [CLK_BUS_BE1] = &bus_be1_clk.common.hw,
+ [CLK_BUS_BE2] = &bus_be2_clk.common.hw,
+ [CLK_BUS_DRC0] = &bus_drc0_clk.common.hw,
+ [CLK_BUS_DRC1] = &bus_drc1_clk.common.hw,
+
+ [CLK_FE0_DIV] = &fe0_div_clk.common.hw,
+ [CLK_FE1_DIV] = &fe1_div_clk.common.hw,
+ [CLK_FE2_DIV] = &fe2_div_clk.common.hw,
+ [CLK_BE0_DIV] = &be0_div_clk.common.hw,
+ [CLK_BE1_DIV] = &be1_div_clk.common.hw,
+ [CLK_BE2_DIV] = &be2_div_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun9i_a80_de_resets[] = {
+ [RST_FE0] = { 0x0c, BIT(0) },
+ [RST_FE1] = { 0x0c, BIT(1) },
+ [RST_FE2] = { 0x0c, BIT(2) },
+ [RST_DEU0] = { 0x0c, BIT(4) },
+ [RST_DEU1] = { 0x0c, BIT(5) },
+ [RST_BE0] = { 0x0c, BIT(8) },
+ [RST_BE1] = { 0x0c, BIT(9) },
+ [RST_BE2] = { 0x0c, BIT(10) },
+ [RST_DRC0] = { 0x0c, BIT(12) },
+ [RST_DRC1] = { 0x0c, BIT(13) },
+ [RST_MERGE] = { 0x0c, BIT(20) },
+};
+
+static const struct sunxi_ccu_desc sun9i_a80_de_clk_desc = {
+ .ccu_clks = sun9i_a80_de_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun9i_a80_de_clks),
+
+ .hw_clks = &sun9i_a80_de_hw_clks,
+
+ .resets = sun9i_a80_de_resets,
+ .num_resets = ARRAY_SIZE(sun9i_a80_de_resets),
+};
+
+static int sun9i_a80_de_clk_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct clk *bus_clk;
+ struct reset_control *rstc;
+ void __iomem *reg;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(bus_clk)) {
+ ret = PTR_ERR(bus_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
+ return ret;
+ }
+
+ rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(rstc)) {
+ ret = PTR_ERR(bus_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Couldn't get reset control: %d\n", ret);
+ return ret;
+ }
+
+ /* The bus clock needs to be enabled for us to access the registers */
+ ret = clk_prepare_enable(bus_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
+ return ret;
+ }
+
+ /* The reset control needs to be asserted for the controls to work */
+ ret = reset_control_deassert(rstc);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Couldn't deassert reset control: %d\n", ret);
+ goto err_disable_clk;
+ }
+
+ ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
+ &sun9i_a80_de_clk_desc);
+ if (ret)
+ goto err_assert_reset;
+
+ return 0;
+
+err_assert_reset:
+ reset_control_assert(rstc);
+err_disable_clk:
+ clk_disable_unprepare(bus_clk);
+ return ret;
+}
+
+static const struct of_device_id sun9i_a80_de_clk_ids[] = {
+ { .compatible = "allwinner,sun9i-a80-de-clks" },
+ { }
+};
+
+static struct platform_driver sun9i_a80_de_clk_driver = {
+ .probe = sun9i_a80_de_clk_probe,
+ .driver = {
+ .name = "sun9i-a80-de-clks",
+ .of_match_table = sun9i_a80_de_clk_ids,
+ },
+};
+builtin_platform_driver(sun9i_a80_de_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h
new file mode 100644
index 000000000000..a4769041e40f
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN9I_A80_DE_H_
+#define _CCU_SUN9I_A80_DE_H_
+
+#include <dt-bindings/clock/sun9i-a80-de.h>
+#include <dt-bindings/reset/sun9i-a80-de.h>
+
+/* Intermediary clock dividers are not exported */
+#define CLK_FE0_DIV 31
+#define CLK_FE1_DIV 32
+#define CLK_FE2_DIV 33
+#define CLK_BE0_DIV 34
+#define CLK_BE1_DIV 35
+#define CLK_BE2_DIV 36
+
+#define CLK_NUMBER (CLK_BE2_DIV + 1)
+
+#endif /* _CCU_SUN9I_A80_DE_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
new file mode 100644
index 000000000000..1d76f24f7df3
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "ccu_common.h"
+#include "ccu_gate.h"
+#include "ccu_reset.h"
+
+#include "ccu-sun9i-a80-usb.h"
+
+static SUNXI_CCU_GATE(bus_hci0_clk, "bus-hci0", "bus-usb", 0x0, BIT(1), 0);
+static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", 0x0, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_hci1_clk, "bus-hci1", "bus-usb", 0x0, BIT(3), 0);
+static SUNXI_CCU_GATE(bus_hci2_clk, "bus-hci2", "bus-usb", 0x0, BIT(5), 0);
+static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc24M", 0x0, BIT(6), 0);
+
+static SUNXI_CCU_GATE(usb0_phy_clk, "usb0-phy", "osc24M", 0x4, BIT(1), 0);
+static SUNXI_CCU_GATE(usb1_hsic_clk, "usb1-hsic", "osc24M", 0x4, BIT(2), 0);
+static SUNXI_CCU_GATE(usb1_phy_clk, "usb1-phy", "osc24M", 0x4, BIT(3), 0);
+static SUNXI_CCU_GATE(usb2_hsic_clk, "usb2-hsic", "osc24M", 0x4, BIT(4), 0);
+static SUNXI_CCU_GATE(usb2_phy_clk, "usb2-phy", "osc24M", 0x4, BIT(5), 0);
+static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "osc24M", 0x4, BIT(10), 0);
+
+static struct ccu_common *sun9i_a80_usb_clks[] = {
+ &bus_hci0_clk.common,
+ &usb_ohci0_clk.common,
+ &bus_hci1_clk.common,
+ &bus_hci2_clk.common,
+ &usb_ohci2_clk.common,
+
+ &usb0_phy_clk.common,
+ &usb1_hsic_clk.common,
+ &usb1_phy_clk.common,
+ &usb2_hsic_clk.common,
+ &usb2_phy_clk.common,
+ &usb_hsic_clk.common,
+};
+
+static struct clk_hw_onecell_data sun9i_a80_usb_hw_clks = {
+ .hws = {
+ [CLK_BUS_HCI0] = &bus_hci0_clk.common.hw,
+ [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw,
+ [CLK_BUS_HCI1] = &bus_hci1_clk.common.hw,
+ [CLK_BUS_HCI2] = &bus_hci2_clk.common.hw,
+ [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw,
+
+ [CLK_USB0_PHY] = &usb0_phy_clk.common.hw,
+ [CLK_USB1_HSIC] = &usb1_hsic_clk.common.hw,
+ [CLK_USB1_PHY] = &usb1_phy_clk.common.hw,
+ [CLK_USB2_HSIC] = &usb2_hsic_clk.common.hw,
+ [CLK_USB2_PHY] = &usb2_phy_clk.common.hw,
+ [CLK_USB_HSIC] = &usb_hsic_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun9i_a80_usb_resets[] = {
+ [RST_USB0_HCI] = { 0x0, BIT(17) },
+ [RST_USB1_HCI] = { 0x0, BIT(18) },
+ [RST_USB2_HCI] = { 0x0, BIT(19) },
+
+ [RST_USB0_PHY] = { 0x4, BIT(17) },
+ [RST_USB1_HSIC] = { 0x4, BIT(18) },
+ [RST_USB1_PHY] = { 0x4, BIT(19) },
+ [RST_USB2_HSIC] = { 0x4, BIT(20) },
+ [RST_USB2_PHY] = { 0x4, BIT(21) },
+};
+
+static const struct sunxi_ccu_desc sun9i_a80_usb_clk_desc = {
+ .ccu_clks = sun9i_a80_usb_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun9i_a80_usb_clks),
+
+ .hw_clks = &sun9i_a80_usb_hw_clks,
+
+ .resets = sun9i_a80_usb_resets,
+ .num_resets = ARRAY_SIZE(sun9i_a80_usb_resets),
+};
+
+static int sun9i_a80_usb_clk_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct clk *bus_clk;
+ void __iomem *reg;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(bus_clk)) {
+ ret = PTR_ERR(bus_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
+ return ret;
+ }
+
+ /* The bus clock needs to be enabled for us to access the registers */
+ ret = clk_prepare_enable(bus_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
+ &sun9i_a80_usb_clk_desc);
+ if (ret)
+ goto err_disable_clk;
+
+ return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(bus_clk);
+ return ret;
+}
+
+static const struct of_device_id sun9i_a80_usb_clk_ids[] = {
+ { .compatible = "allwinner,sun9i-a80-usb-clks" },
+ { }
+};
+
+static struct platform_driver sun9i_a80_usb_clk_driver = {
+ .probe = sun9i_a80_usb_clk_probe,
+ .driver = {
+ .name = "sun9i-a80-usb-clks",
+ .of_match_table = sun9i_a80_usb_clk_ids,
+ },
+};
+builtin_platform_driver(sun9i_a80_usb_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h
new file mode 100644
index 000000000000..a184280ba854
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN9I_A80_USB_H_
+#define _CCU_SUN9I_A80_USB_H_
+
+#include <dt-bindings/clock/sun9i-a80-usb.h>
+#include <dt-bindings/reset/sun9i-a80-usb.h>
+
+#define CLK_NUMBER (CLK_USB_HSIC + 1)
+
+#endif /* _CCU_SUN9I_A80_USB_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
new file mode 100644
index 000000000000..e13e313ce4f5
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
@@ -0,0 +1,1223 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun9i-a80.h"
+
+#define CCU_SUN9I_LOCK_REG 0x09c
+
+static struct clk_div_table pll_cpux_p_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 4 },
+ { /* Sentinel */ },
+};
+
+/*
+ * The CPU PLLs are actually NP clocks, but P is /1 or /4, so here we
+ * use the NM clocks with a divider table for M.
+ */
+static struct ccu_nm pll_c0cpux_clk = {
+ .enable = BIT(31),
+ .lock = BIT(0),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV_TABLE(16, 1, pll_cpux_p_div_table),
+ .common = {
+ .reg = 0x000,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-c0cpux", "osc24M",
+ &ccu_nm_ops, CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nm pll_c1cpux_clk = {
+ .enable = BIT(31),
+ .lock = BIT(1),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV_TABLE(16, 1, pll_cpux_p_div_table),
+ .common = {
+ .reg = 0x004,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-c1cpux", "osc24M",
+ &ccu_nm_ops, CLK_SET_RATE_UNGATE),
+ },
+};
+
+/*
+ * The Audio PLL has d1, d2 dividers in addition to the usual N, M
+ * factors. Since we only need 2 frequencies from this PLL: 22.5792 MHz
+ * and 24.576 MHz, ignore them for now. Enforce the default for them,
+ * which is d1 = 0, d2 = 1.
+ */
+#define SUN9I_A80_PLL_AUDIO_REG 0x008
+
+static struct ccu_nm pll_audio_clk = {
+ .enable = BIT(31),
+ .lock = BIT(2),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV_OFFSET(0, 6, 0),
+ .common = {
+ .reg = 0x008,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-audio", "osc24M",
+ &ccu_nm_ops, CLK_SET_RATE_UNGATE),
+ },
+};
+
+/* Some PLLs are input * N / div1 / div2. Model them as NKMP with no K */
+static struct ccu_nkmp pll_periph0_clk = {
+ .enable = BIT(31),
+ .lock = BIT(3),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x00c,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-periph0", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_ve_clk = {
+ .enable = BIT(31),
+ .lock = BIT(4),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x010,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-ve", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_ddr_clk = {
+ .enable = BIT(31),
+ .lock = BIT(5),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x014,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-ddr", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nm pll_video0_clk = {
+ .enable = BIT(31),
+ .lock = BIT(6),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .common = {
+ .reg = 0x018,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-video0", "osc24M",
+ &ccu_nm_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_video1_clk = {
+ .enable = BIT(31),
+ .lock = BIT(7),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 2), /* external divider p */
+ .common = {
+ .reg = 0x01c,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-video1", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_gpu_clk = {
+ .enable = BIT(31),
+ .lock = BIT(8),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x020,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-gpu", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_de_clk = {
+ .enable = BIT(31),
+ .lock = BIT(9),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x024,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-de", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_isp_clk = {
+ .enable = BIT(31),
+ .lock = BIT(10),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x028,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-isp", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static struct ccu_nkmp pll_periph1_clk = {
+ .enable = BIT(31),
+ .lock = BIT(11),
+ .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
+ .m = _SUNXI_CCU_DIV(16, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(18, 1), /* output divider */
+ .common = {
+ .reg = 0x028,
+ .lock_reg = CCU_SUN9I_LOCK_REG,
+ .features = CCU_FEATURE_LOCK_REG,
+ .hw.init = CLK_HW_INIT("pll-periph1", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static const char * const c0cpux_parents[] = { "osc24M", "pll-c0cpux" };
+static SUNXI_CCU_MUX(c0cpux_clk, "c0cpux", c0cpux_parents,
+ 0x50, 0, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+static const char * const c1cpux_parents[] = { "osc24M", "pll-c1cpux" };
+static SUNXI_CCU_MUX(c1cpux_clk, "c1cpux", c1cpux_parents,
+ 0x50, 8, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+static struct clk_div_table axi_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 4 },
+ { .val = 5, .div = 4 },
+ { .val = 6, .div = 4 },
+ { .val = 7, .div = 4 },
+ { /* Sentinel */ },
+};
+
+static SUNXI_CCU_M(atb0_clk, "atb0", "c0cpux", 0x054, 8, 2, 0);
+
+static SUNXI_CCU_DIV_TABLE(axi0_clk, "axi0", "c0cpux",
+ 0x054, 0, 3, axi_div_table, 0);
+
+static SUNXI_CCU_M(atb1_clk, "atb1", "c1cpux", 0x058, 8, 2, 0);
+
+static SUNXI_CCU_DIV_TABLE(axi1_clk, "axi1", "c1cpux",
+ 0x058, 0, 3, axi_div_table, 0);
+
+static const char * const gtbus_parents[] = { "osc24M", "pll-periph0",
+ "pll-periph1", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX(gtbus_clk, "gtbus", gtbus_parents,
+ 0x05c, 0, 2, 24, 2, CLK_IS_CRITICAL);
+
+static const char * const ahb_parents[] = { "gtbus", "pll-periph0",
+ "pll-periph1", "pll-periph1" };
+static struct ccu_div ahb0_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0x060,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb0",
+ ahb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div ahb1_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0x064,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb1",
+ ahb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div ahb2_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0x068,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb2",
+ ahb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const char * const apb_parents[] = { "osc24M", "pll-periph0" };
+
+static struct ccu_div apb0_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 1),
+ .common = {
+ .reg = 0x070,
+ .hw.init = CLK_HW_INIT_PARENTS("apb0",
+ apb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div apb1_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 1),
+ .common = {
+ .reg = 0x074,
+ .hw.init = CLK_HW_INIT_PARENTS("apb1",
+ apb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div cci400_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0x078,
+ .hw.init = CLK_HW_INIT_PARENTS("cci400",
+ ahb_parents,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", apb_parents,
+ 0x080, 0, 3, 24, 2, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(trace_clk, "trace", apb_parents,
+ 0x084, 0, 3, 24, 2, BIT(31), 0);
+
+static const char * const out_parents[] = { "osc24M", "osc32k", "osc24M" };
+static const struct ccu_mux_fixed_prediv out_prediv = {
+ .index = 0, .div = 750
+};
+
+static struct ccu_mp out_a_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(8, 5),
+ .p = _SUNXI_CCU_DIV(20, 2),
+ .mux = {
+ .shift = 24,
+ .width = 4,
+ .fixed_predivs = &out_prediv,
+ .n_predivs = 1,
+ },
+ .common = {
+ .reg = 0x180,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("out-a",
+ out_parents,
+ &ccu_mp_ops,
+ 0),
+ },
+};
+
+static struct ccu_mp out_b_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(8, 5),
+ .p = _SUNXI_CCU_DIV(20, 2),
+ .mux = {
+ .shift = 24,
+ .width = 4,
+ .fixed_predivs = &out_prediv,
+ .n_predivs = 1,
+ },
+ .common = {
+ .reg = 0x184,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("out-b",
+ out_parents,
+ &ccu_mp_ops,
+ 0),
+ },
+};
+
+static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0" };
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_0_clk, "nand0-0", mod0_default_parents,
+ 0x400,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_1_clk, "nand0-1", mod0_default_parents,
+ 0x404,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_0_clk, "nand1-0", mod0_default_parents,
+ 0x408,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_1_clk, "nand1-1", mod0_default_parents,
+ 0x40c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents,
+ 0x410,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0-sample", "mmc0",
+ 0x410, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0-output", "mmc0",
+ 0x410, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents,
+ 0x414,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1-sample", "mmc1",
+ 0x414, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1-output", "mmc1",
+ 0x414, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents,
+ 0x418,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2-sample", "mmc2",
+ 0x418, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2-output", "mmc2",
+ 0x418, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents,
+ 0x41c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc3_sample_clk, "mmc3-sample", "mmc3",
+ 0x41c, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc3_output_clk, "mmc3-output", "mmc3",
+ 0x41c, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents,
+ 0x428,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const ss_parents[] = { "osc24M", "pll-periph",
+ "pll-periph1" };
+static const u8 ss_table[] = { 0, 1, 13 };
+static struct ccu_mp ss_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(0, 4),
+ .p = _SUNXI_CCU_DIV(16, 2),
+ .mux = _SUNXI_CCU_MUX_TABLE(24, 4, ss_table),
+ .common = {
+ .reg = 0x42c,
+ .hw.init = CLK_HW_INIT_PARENTS("ss",
+ ss_parents,
+ &ccu_mp_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents,
+ 0x430,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents,
+ 0x434,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents,
+ 0x438,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents,
+ 0x43c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_M_WITH_GATE(i2s0_clk, "i2s0", "pll-audio",
+ 0x440, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M_WITH_GATE(i2s1_clk, "i2s1", "pll-audio",
+ 0x444, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio",
+ 0x44c, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+
+static const char * const sdram_parents[] = { "pll-periph0", "pll-ddr" };
+static const u8 sdram_table[] = { 0, 3 };
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(sdram_clk, "sdram",
+ sdram_parents, sdram_table,
+ 0x484,
+ 8, 4, /* M */
+ 12, 4, /* mux */
+ 0, /* no gate */
+ CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M_WITH_GATE(de_clk, "de", "pll-de", 0x490,
+ 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(edp_clk, "edp", "osc24M", 0x494, BIT(31), 0);
+
+static const char * const mp_parents[] = { "pll-video1", "pll-gpu", "pll-de" };
+static const u8 mp_table[] = { 9, 10, 11 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mp_clk, "mp", mp_parents, mp_table,
+ 0x498,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const display_parents[] = { "pll-video0", "pll-video1" };
+static const u8 display_table[] = { 8, 9 };
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd0_clk, "lcd0",
+ display_parents, display_table,
+ 0x49c,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_NO_REPARENT |
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd1_clk, "lcd1",
+ display_parents, display_table,
+ 0x4a0,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_NO_REPARENT |
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mipi_dsi0_clk, "mipi-dsi0",
+ display_parents, display_table,
+ 0x4a8,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_PARENT);
+
+static const char * const mipi_dsi1_parents[] = { "osc24M", "pll-video1" };
+static const u8 mipi_dsi1_table[] = { 0, 9 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mipi_dsi1_clk, "mipi-dsi1",
+ mipi_dsi1_parents, mipi_dsi1_table,
+ 0x4ac,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(hdmi_clk, "hdmi",
+ display_parents, display_table,
+ 0x4b0,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_NO_REPARENT |
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", 0x4b4, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(mipi_csi_clk, "mipi-csi", "osc24M", 0x4bc,
+ 0, 4, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(csi_isp_clk, "csi-isp", "pll-isp", 0x4c0,
+ 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", 0x4c0, BIT(16), 0);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi0_mclk_clk, "csi0-mclk",
+ mipi_dsi1_parents, mipi_dsi1_table,
+ 0x4c4,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi1_mclk_clk, "csi1-mclk",
+ mipi_dsi1_parents, mipi_dsi1_table,
+ 0x4c8,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_PARENT);
+
+static const char * const fd_parents[] = { "pll-periph0", "pll-isp" };
+static const u8 fd_table[] = { 1, 12 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(fd_clk, "fd", fd_parents, fd_table,
+ 0x4cc,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x4d0,
+ 16, 3, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x4d4, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(gpu_core_clk, "gpu-core", "pll-gpu", 0x4f0,
+ 0, 3, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M_WITH_GATE(gpu_memory_clk, "gpu-memory", "pll-gpu", 0x4f4,
+ 0, 3, BIT(31), CLK_SET_RATE_PARENT);
+
+static const char * const gpu_axi_parents[] = { "pll-periph0", "pll-gpu" };
+static const u8 gpu_axi_table[] = { 1, 10 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(gpu_axi_clk, "gpu-axi",
+ gpu_axi_parents, gpu_axi_table,
+ 0x4f8,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_GATE(sata_clk, "sata", "pll-periph0", 0x500,
+ 0, 4, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(ac97_clk, "ac97", "pll-audio",
+ 0x504, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(mipi_hsi_clk, "mipi-hsi",
+ mod0_default_parents, 0x508,
+ 0, 4, /* M */
+ 24, 4, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const gpadc_parents[] = { "osc24M", "pll-audio", "osc32k" };
+static const u8 gpadc_table[] = { 0, 4, 7 };
+static struct ccu_mp gpadc_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(0, 4),
+ .p = _SUNXI_CCU_DIV(16, 2),
+ .mux = _SUNXI_CCU_MUX_TABLE(24, 4, gpadc_table),
+ .common = {
+ .reg = 0x50c,
+ .hw.init = CLK_HW_INIT_PARENTS("gpadc",
+ gpadc_parents,
+ &ccu_mp_ops,
+ 0),
+ },
+};
+
+static const char * const cir_tx_parents[] = { "osc24M", "osc32k" };
+static const u8 cir_tx_table[] = { 0, 7 };
+static struct ccu_mp cir_tx_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(0, 4),
+ .p = _SUNXI_CCU_DIV(16, 2),
+ .mux = _SUNXI_CCU_MUX_TABLE(24, 4, cir_tx_table),
+ .common = {
+ .reg = 0x510,
+ .hw.init = CLK_HW_INIT_PARENTS("cir-tx",
+ cir_tx_parents,
+ &ccu_mp_ops,
+ 0),
+ },
+};
+
+/* AHB0 bus gates */
+static SUNXI_CCU_GATE(bus_fd_clk, "bus-fd", "ahb0",
+ 0x580, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb0",
+ 0x580, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_gpu_ctrl_clk, "bus-gpu-ctrl", "ahb0",
+ 0x580, BIT(3), 0);
+static SUNXI_CCU_GATE(bus_ss_clk, "bus-ss", "ahb0",
+ 0x580, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_mmc_clk, "bus-mmc", "ahb0",
+ 0x580, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_nand0_clk, "bus-nand0", "ahb0",
+ 0x580, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_nand1_clk, "bus-nand1", "ahb0",
+ 0x580, BIT(13), 0);
+static SUNXI_CCU_GATE(bus_sdram_clk, "bus-sdram", "ahb0",
+ 0x580, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_mipi_hsi_clk, "bus-mipi-hsi", "ahb0",
+ 0x580, BIT(15), 0);
+static SUNXI_CCU_GATE(bus_sata_clk, "bus-sata", "ahb0",
+ 0x580, BIT(16), 0);
+static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb0",
+ 0x580, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb0",
+ 0x580, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb0",
+ 0x580, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_spi2_clk, "bus-spi2", "ahb0",
+ 0x580, BIT(22), 0);
+static SUNXI_CCU_GATE(bus_spi3_clk, "bus-spi3", "ahb0",
+ 0x580, BIT(23), 0);
+
+/* AHB1 bus gates */
+static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1",
+ 0x584, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_usb_clk, "bus-usb", "ahb1",
+ 0x584, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_gmac_clk, "bus-gmac", "ahb1",
+ 0x584, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1",
+ 0x584, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1",
+ 0x584, BIT(22), 0);
+static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1",
+ 0x584, BIT(23), 0);
+static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1",
+ 0x584, BIT(24), 0);
+
+/* AHB2 bus gates */
+static SUNXI_CCU_GATE(bus_lcd0_clk, "bus-lcd0", "ahb2",
+ 0x588, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_lcd1_clk, "bus-lcd1", "ahb2",
+ 0x588, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_edp_clk, "bus-edp", "ahb2",
+ 0x588, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb2",
+ 0x588, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_hdmi_clk, "bus-hdmi", "ahb2",
+ 0x588, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb2",
+ 0x588, BIT(7), 0);
+static SUNXI_CCU_GATE(bus_mp_clk, "bus-mp", "ahb2",
+ 0x588, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb2",
+ 0x588, BIT(11), 0);
+
+/* APB0 bus gates */
+static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb0",
+ 0x590, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb0",
+ 0x590, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_ac97_clk, "bus-ac97", "apb0",
+ 0x590, BIT(11), 0);
+static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb0",
+ 0x590, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb0",
+ 0x590, BIT(13), 0);
+static SUNXI_CCU_GATE(bus_lradc_clk, "bus-lradc", "apb0",
+ 0x590, BIT(15), 0);
+static SUNXI_CCU_GATE(bus_gpadc_clk, "bus-gpadc", "apb0",
+ 0x590, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_twd_clk, "bus-twd", "apb0",
+ 0x590, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_cir_tx_clk, "bus-cir-tx", "apb0",
+ 0x590, BIT(19), 0);
+
+/* APB1 bus gates */
+static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb1",
+ 0x594, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb1",
+ 0x594, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb1",
+ 0x594, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_i2c3_clk, "bus-i2c3", "apb1",
+ 0x594, BIT(3), 0);
+static SUNXI_CCU_GATE(bus_i2c4_clk, "bus-i2c4", "apb1",
+ 0x594, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb1",
+ 0x594, BIT(16), 0);
+static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb1",
+ 0x594, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb1",
+ 0x594, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb1",
+ 0x594, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb1",
+ 0x594, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_uart5_clk, "bus-uart5", "apb1",
+ 0x594, BIT(21), 0);
+
+static struct ccu_common *sun9i_a80_ccu_clks[] = {
+ &pll_c0cpux_clk.common,
+ &pll_c1cpux_clk.common,
+ &pll_audio_clk.common,
+ &pll_periph0_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr_clk.common,
+ &pll_video0_clk.common,
+ &pll_video1_clk.common,
+ &pll_gpu_clk.common,
+ &pll_de_clk.common,
+ &pll_isp_clk.common,
+ &pll_periph1_clk.common,
+ &c0cpux_clk.common,
+ &c1cpux_clk.common,
+ &atb0_clk.common,
+ &axi0_clk.common,
+ &atb1_clk.common,
+ &axi1_clk.common,
+ &gtbus_clk.common,
+ &ahb0_clk.common,
+ &ahb1_clk.common,
+ &ahb2_clk.common,
+ &apb0_clk.common,
+ &apb1_clk.common,
+ &cci400_clk.common,
+ &ats_clk.common,
+ &trace_clk.common,
+
+ &out_a_clk.common,
+ &out_b_clk.common,
+
+ /* module clocks */
+ &nand0_0_clk.common,
+ &nand0_1_clk.common,
+ &nand1_0_clk.common,
+ &nand1_1_clk.common,
+ &mmc0_clk.common,
+ &mmc0_sample_clk.common,
+ &mmc0_output_clk.common,
+ &mmc1_clk.common,
+ &mmc1_sample_clk.common,
+ &mmc1_output_clk.common,
+ &mmc2_clk.common,
+ &mmc2_sample_clk.common,
+ &mmc2_output_clk.common,
+ &mmc3_clk.common,
+ &mmc3_sample_clk.common,
+ &mmc3_output_clk.common,
+ &ts_clk.common,
+ &ss_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &spi2_clk.common,
+ &spi3_clk.common,
+ &i2s0_clk.common,
+ &i2s1_clk.common,
+ &spdif_clk.common,
+ &sdram_clk.common,
+ &de_clk.common,
+ &edp_clk.common,
+ &mp_clk.common,
+ &lcd0_clk.common,
+ &lcd1_clk.common,
+ &mipi_dsi0_clk.common,
+ &mipi_dsi1_clk.common,
+ &hdmi_clk.common,
+ &hdmi_slow_clk.common,
+ &mipi_csi_clk.common,
+ &csi_isp_clk.common,
+ &csi_misc_clk.common,
+ &csi0_mclk_clk.common,
+ &csi1_mclk_clk.common,
+ &fd_clk.common,
+ &ve_clk.common,
+ &avs_clk.common,
+ &gpu_core_clk.common,
+ &gpu_memory_clk.common,
+ &gpu_axi_clk.common,
+ &sata_clk.common,
+ &ac97_clk.common,
+ &mipi_hsi_clk.common,
+ &gpadc_clk.common,
+ &cir_tx_clk.common,
+
+ /* AHB0 bus gates */
+ &bus_fd_clk.common,
+ &bus_ve_clk.common,
+ &bus_gpu_ctrl_clk.common,
+ &bus_ss_clk.common,
+ &bus_mmc_clk.common,
+ &bus_nand0_clk.common,
+ &bus_nand1_clk.common,
+ &bus_sdram_clk.common,
+ &bus_mipi_hsi_clk.common,
+ &bus_sata_clk.common,
+ &bus_ts_clk.common,
+ &bus_spi0_clk.common,
+ &bus_spi1_clk.common,
+ &bus_spi2_clk.common,
+ &bus_spi3_clk.common,
+
+ /* AHB1 bus gates */
+ &bus_otg_clk.common,
+ &bus_usb_clk.common,
+ &bus_gmac_clk.common,
+ &bus_msgbox_clk.common,
+ &bus_spinlock_clk.common,
+ &bus_hstimer_clk.common,
+ &bus_dma_clk.common,
+
+ /* AHB2 bus gates */
+ &bus_lcd0_clk.common,
+ &bus_lcd1_clk.common,
+ &bus_edp_clk.common,
+ &bus_csi_clk.common,
+ &bus_hdmi_clk.common,
+ &bus_de_clk.common,
+ &bus_mp_clk.common,
+ &bus_mipi_dsi_clk.common,
+
+ /* APB0 bus gates */
+ &bus_spdif_clk.common,
+ &bus_pio_clk.common,
+ &bus_ac97_clk.common,
+ &bus_i2s0_clk.common,
+ &bus_i2s1_clk.common,
+ &bus_lradc_clk.common,
+ &bus_gpadc_clk.common,
+ &bus_twd_clk.common,
+ &bus_cir_tx_clk.common,
+
+ /* APB1 bus gates */
+ &bus_i2c0_clk.common,
+ &bus_i2c1_clk.common,
+ &bus_i2c2_clk.common,
+ &bus_i2c3_clk.common,
+ &bus_i2c4_clk.common,
+ &bus_uart0_clk.common,
+ &bus_uart1_clk.common,
+ &bus_uart2_clk.common,
+ &bus_uart3_clk.common,
+ &bus_uart4_clk.common,
+ &bus_uart5_clk.common,
+};
+
+static struct clk_hw_onecell_data sun9i_a80_hw_clks = {
+ .hws = {
+ [CLK_PLL_C0CPUX] = &pll_c0cpux_clk.common.hw,
+ [CLK_PLL_C1CPUX] = &pll_c1cpux_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.common.hw,
+ [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw,
+ [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw,
+ [CLK_PLL_GPU] = &pll_gpu_clk.common.hw,
+ [CLK_PLL_DE] = &pll_de_clk.common.hw,
+ [CLK_PLL_ISP] = &pll_isp_clk.common.hw,
+ [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw,
+ [CLK_C0CPUX] = &c0cpux_clk.common.hw,
+ [CLK_C1CPUX] = &c1cpux_clk.common.hw,
+ [CLK_ATB0] = &atb0_clk.common.hw,
+ [CLK_AXI0] = &axi0_clk.common.hw,
+ [CLK_ATB1] = &atb1_clk.common.hw,
+ [CLK_AXI1] = &axi1_clk.common.hw,
+ [CLK_GTBUS] = &gtbus_clk.common.hw,
+ [CLK_AHB0] = &ahb0_clk.common.hw,
+ [CLK_AHB1] = &ahb1_clk.common.hw,
+ [CLK_AHB2] = &ahb2_clk.common.hw,
+ [CLK_APB0] = &apb0_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_CCI400] = &cci400_clk.common.hw,
+ [CLK_ATS] = &ats_clk.common.hw,
+ [CLK_TRACE] = &trace_clk.common.hw,
+
+ [CLK_OUT_A] = &out_a_clk.common.hw,
+ [CLK_OUT_B] = &out_b_clk.common.hw,
+
+ [CLK_NAND0_0] = &nand0_0_clk.common.hw,
+ [CLK_NAND0_1] = &nand0_1_clk.common.hw,
+ [CLK_NAND1_0] = &nand1_0_clk.common.hw,
+ [CLK_NAND1_1] = &nand1_1_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw,
+ [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw,
+ [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw,
+ [CLK_MMC3] = &mmc3_clk.common.hw,
+ [CLK_MMC3_SAMPLE] = &mmc3_sample_clk.common.hw,
+ [CLK_MMC3_OUTPUT] = &mmc3_output_clk.common.hw,
+ [CLK_TS] = &ts_clk.common.hw,
+ [CLK_SS] = &ss_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_SPI2] = &spi2_clk.common.hw,
+ [CLK_SPI3] = &spi3_clk.common.hw,
+ [CLK_I2S0] = &i2s0_clk.common.hw,
+ [CLK_I2S1] = &i2s1_clk.common.hw,
+ [CLK_SPDIF] = &spdif_clk.common.hw,
+ [CLK_SDRAM] = &sdram_clk.common.hw,
+ [CLK_DE] = &de_clk.common.hw,
+ [CLK_EDP] = &edp_clk.common.hw,
+ [CLK_MP] = &mp_clk.common.hw,
+ [CLK_LCD0] = &lcd0_clk.common.hw,
+ [CLK_LCD1] = &lcd1_clk.common.hw,
+ [CLK_MIPI_DSI0] = &mipi_dsi0_clk.common.hw,
+ [CLK_MIPI_DSI1] = &mipi_dsi1_clk.common.hw,
+ [CLK_HDMI] = &hdmi_clk.common.hw,
+ [CLK_HDMI_SLOW] = &hdmi_slow_clk.common.hw,
+ [CLK_MIPI_CSI] = &mipi_csi_clk.common.hw,
+ [CLK_CSI_ISP] = &csi_isp_clk.common.hw,
+ [CLK_CSI_MISC] = &csi_misc_clk.common.hw,
+ [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw,
+ [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw,
+ [CLK_FD] = &fd_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_GPU_CORE] = &gpu_core_clk.common.hw,
+ [CLK_GPU_MEMORY] = &gpu_memory_clk.common.hw,
+ [CLK_GPU_AXI] = &gpu_axi_clk.common.hw,
+ [CLK_SATA] = &sata_clk.common.hw,
+ [CLK_AC97] = &ac97_clk.common.hw,
+ [CLK_MIPI_HSI] = &mipi_hsi_clk.common.hw,
+ [CLK_GPADC] = &gpadc_clk.common.hw,
+ [CLK_CIR_TX] = &cir_tx_clk.common.hw,
+
+ [CLK_BUS_FD] = &bus_fd_clk.common.hw,
+ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
+ [CLK_BUS_GPU_CTRL] = &bus_gpu_ctrl_clk.common.hw,
+ [CLK_BUS_SS] = &bus_ss_clk.common.hw,
+ [CLK_BUS_MMC] = &bus_mmc_clk.common.hw,
+ [CLK_BUS_NAND0] = &bus_nand0_clk.common.hw,
+ [CLK_BUS_NAND1] = &bus_nand1_clk.common.hw,
+ [CLK_BUS_SDRAM] = &bus_sdram_clk.common.hw,
+ [CLK_BUS_MIPI_HSI] = &bus_mipi_hsi_clk.common.hw,
+ [CLK_BUS_SATA] = &bus_sata_clk.common.hw,
+ [CLK_BUS_TS] = &bus_ts_clk.common.hw,
+ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
+ [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw,
+ [CLK_BUS_SPI2] = &bus_spi2_clk.common.hw,
+ [CLK_BUS_SPI3] = &bus_spi3_clk.common.hw,
+
+ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
+ [CLK_BUS_USB] = &bus_usb_clk.common.hw,
+ [CLK_BUS_GMAC] = &bus_gmac_clk.common.hw,
+ [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw,
+ [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw,
+ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
+ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
+
+ [CLK_BUS_LCD0] = &bus_lcd0_clk.common.hw,
+ [CLK_BUS_LCD1] = &bus_lcd1_clk.common.hw,
+ [CLK_BUS_EDP] = &bus_edp_clk.common.hw,
+ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
+ [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw,
+ [CLK_BUS_DE] = &bus_de_clk.common.hw,
+ [CLK_BUS_MP] = &bus_mp_clk.common.hw,
+ [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw,
+
+ [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw,
+ [CLK_BUS_PIO] = &bus_pio_clk.common.hw,
+ [CLK_BUS_AC97] = &bus_ac97_clk.common.hw,
+ [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw,
+ [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw,
+ [CLK_BUS_LRADC] = &bus_lradc_clk.common.hw,
+ [CLK_BUS_GPADC] = &bus_gpadc_clk.common.hw,
+ [CLK_BUS_TWD] = &bus_twd_clk.common.hw,
+ [CLK_BUS_CIR_TX] = &bus_cir_tx_clk.common.hw,
+
+ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
+ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
+ [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw,
+ [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw,
+ [CLK_BUS_I2C4] = &bus_i2c4_clk.common.hw,
+ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
+ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
+ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
+ [CLK_BUS_UART3] = &bus_uart3_clk.common.hw,
+ [CLK_BUS_UART4] = &bus_uart4_clk.common.hw,
+ [CLK_BUS_UART5] = &bus_uart5_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun9i_a80_ccu_resets[] = {
+ /* AHB0 reset controls */
+ [RST_BUS_FD] = { 0x5a0, BIT(0) },
+ [RST_BUS_VE] = { 0x5a0, BIT(1) },
+ [RST_BUS_GPU_CTRL] = { 0x5a0, BIT(3) },
+ [RST_BUS_SS] = { 0x5a0, BIT(5) },
+ [RST_BUS_MMC] = { 0x5a0, BIT(8) },
+ [RST_BUS_NAND0] = { 0x5a0, BIT(12) },
+ [RST_BUS_NAND1] = { 0x5a0, BIT(13) },
+ [RST_BUS_SDRAM] = { 0x5a0, BIT(14) },
+ [RST_BUS_SATA] = { 0x5a0, BIT(16) },
+ [RST_BUS_TS] = { 0x5a0, BIT(18) },
+ [RST_BUS_SPI0] = { 0x5a0, BIT(20) },
+ [RST_BUS_SPI1] = { 0x5a0, BIT(21) },
+ [RST_BUS_SPI2] = { 0x5a0, BIT(22) },
+ [RST_BUS_SPI3] = { 0x5a0, BIT(23) },
+
+ /* AHB1 reset controls */
+ [RST_BUS_OTG] = { 0x5a4, BIT(0) },
+ [RST_BUS_OTG_PHY] = { 0x5a4, BIT(1) },
+ [RST_BUS_MIPI_HSI] = { 0x5a4, BIT(9) },
+ [RST_BUS_GMAC] = { 0x5a4, BIT(17) },
+ [RST_BUS_MSGBOX] = { 0x5a4, BIT(21) },
+ [RST_BUS_SPINLOCK] = { 0x5a4, BIT(22) },
+ [RST_BUS_HSTIMER] = { 0x5a4, BIT(23) },
+ [RST_BUS_DMA] = { 0x5a4, BIT(24) },
+
+ /* AHB2 reset controls */
+ [RST_BUS_LCD0] = { 0x5a8, BIT(0) },
+ [RST_BUS_LCD1] = { 0x5a8, BIT(1) },
+ [RST_BUS_EDP] = { 0x5a8, BIT(2) },
+ [RST_BUS_LVDS] = { 0x5a8, BIT(3) },
+ [RST_BUS_CSI] = { 0x5a8, BIT(4) },
+ [RST_BUS_HDMI0] = { 0x5a8, BIT(5) },
+ [RST_BUS_HDMI1] = { 0x5a8, BIT(6) },
+ [RST_BUS_DE] = { 0x5a8, BIT(7) },
+ [RST_BUS_MP] = { 0x5a8, BIT(8) },
+ [RST_BUS_GPU] = { 0x5a8, BIT(9) },
+ [RST_BUS_MIPI_DSI] = { 0x5a8, BIT(11) },
+
+ /* APB0 reset controls */
+ [RST_BUS_SPDIF] = { 0x5b0, BIT(1) },
+ [RST_BUS_AC97] = { 0x5b0, BIT(11) },
+ [RST_BUS_I2S0] = { 0x5b0, BIT(12) },
+ [RST_BUS_I2S1] = { 0x5b0, BIT(13) },
+ [RST_BUS_LRADC] = { 0x5b0, BIT(15) },
+ [RST_BUS_GPADC] = { 0x5b0, BIT(17) },
+ [RST_BUS_CIR_TX] = { 0x5b0, BIT(19) },
+
+ /* APB1 reset controls */
+ [RST_BUS_I2C0] = { 0x5b4, BIT(0) },
+ [RST_BUS_I2C1] = { 0x5b4, BIT(1) },
+ [RST_BUS_I2C2] = { 0x5b4, BIT(2) },
+ [RST_BUS_I2C3] = { 0x5b4, BIT(3) },
+ [RST_BUS_I2C4] = { 0x5b4, BIT(4) },
+ [RST_BUS_UART0] = { 0x5b4, BIT(16) },
+ [RST_BUS_UART1] = { 0x5b4, BIT(17) },
+ [RST_BUS_UART2] = { 0x5b4, BIT(18) },
+ [RST_BUS_UART3] = { 0x5b4, BIT(19) },
+ [RST_BUS_UART4] = { 0x5b4, BIT(20) },
+ [RST_BUS_UART5] = { 0x5b4, BIT(21) },
+};
+
+static const struct sunxi_ccu_desc sun9i_a80_ccu_desc = {
+ .ccu_clks = sun9i_a80_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun9i_a80_ccu_clks),
+
+ .hw_clks = &sun9i_a80_hw_clks,
+
+ .resets = sun9i_a80_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun9i_a80_ccu_resets),
+};
+
+static int sun9i_a80_ccu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *reg;
+ u32 val;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ /* Enforce d1 = 0, d2 = 0 for Audio PLL */
+ val = readl(reg + SUN9I_A80_PLL_AUDIO_REG);
+ val &= (BIT(16) & BIT(18));
+ writel(val, reg + SUN9I_A80_PLL_AUDIO_REG);
+
+ return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc);
+}
+
+static const struct of_device_id sun9i_a80_ccu_ids[] = {
+ { .compatible = "allwinner,sun9i-a80-ccu" },
+ { }
+};
+
+static struct platform_driver sun9i_a80_ccu_driver = {
+ .probe = sun9i_a80_ccu_probe,
+ .driver = {
+ .name = "sun9i-a80-ccu",
+ .of_match_table = sun9i_a80_ccu_ids,
+ },
+};
+builtin_platform_driver(sun9i_a80_ccu_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80.h
new file mode 100644
index 000000000000..315662341c70
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN9I_A80_H_
+#define _CCU_SUN9I_A80_H_
+
+#include <dt-bindings/clock/sun9i-a80-ccu.h>
+#include <dt-bindings/reset/sun9i-a80-ccu.h>
+
+#define CLK_PLL_C0CPUX 0
+#define CLK_PLL_C1CPUX 1
+
+/* pll-audio and pll-periph0 are exported to the PRCM block */
+
+#define CLK_PLL_VE 4
+#define CLK_PLL_DDR 5
+#define CLK_PLL_VIDEO0 6
+#define CLK_PLL_VIDEO1 7
+#define CLK_PLL_GPU 8
+#define CLK_PLL_DE 9
+#define CLK_PLL_ISP 10
+#define CLK_PLL_PERIPH1 11
+
+/* The CPUX clocks are exported */
+
+#define CLK_ATB0 14
+#define CLK_AXI0 15
+#define CLK_ATB1 16
+#define CLK_AXI1 17
+#define CLK_GTBUS 18
+#define CLK_AHB0 19
+#define CLK_AHB1 20
+#define CLK_AHB2 21
+#define CLK_APB0 22
+#define CLK_APB1 23
+#define CLK_CCI400 24
+#define CLK_ATS 25
+#define CLK_TRACE 26
+
+/* module clocks and bus gates exported */
+
+#define CLK_NUMBER (CLK_BUS_UART5 + 1)
+
+#endif /* _CCU_SUN9I_A80_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c
index 51d4bac97ab3..6986e11e91b0 100644
--- a/drivers/clk/sunxi-ng/ccu_common.c
+++ b/drivers/clk/sunxi-ng/ccu_common.c
@@ -25,13 +25,18 @@ static DEFINE_SPINLOCK(ccu_lock);
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
{
+ void __iomem *addr;
u32 reg;
if (!lock)
return;
- WARN_ON(readl_relaxed_poll_timeout(common->base + common->reg, reg,
- reg & lock, 100, 70000));
+ if (common->features & CCU_FEATURE_LOCK_REG)
+ addr = common->base + common->lock_reg;
+ else
+ addr = common->base + common->reg;
+
+ WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000));
}
int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h
index b3d9abfbd721..73d81dc58fc5 100644
--- a/drivers/clk/sunxi-ng/ccu_common.h
+++ b/drivers/clk/sunxi-ng/ccu_common.h
@@ -21,6 +21,8 @@
#define CCU_FEATURE_VARIABLE_PREDIV BIT(1)
#define CCU_FEATURE_FIXED_PREDIV BIT(2)
#define CCU_FEATURE_FIXED_POSTDIV BIT(3)
+#define CCU_FEATURE_ALL_PREDIV BIT(4)
+#define CCU_FEATURE_LOCK_REG BIT(5)
struct device_node;
@@ -56,6 +58,8 @@ struct device_node;
struct ccu_common {
void __iomem *base;
u16 reg;
+ u16 lock_reg;
+ u32 prediv;
unsigned long features;
spinlock_t *lock;
diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
index 8659b4cb6c20..4057e6021aa9 100644
--- a/drivers/clk/sunxi-ng/ccu_div.c
+++ b/drivers/clk/sunxi-ng/ccu_div.c
@@ -77,6 +77,18 @@ static int ccu_div_determine_rate(struct clk_hw *hw,
{
struct ccu_div *cd = hw_to_ccu_div(hw);
+ if (clk_hw_get_num_parents(hw) == 1) {
+ req->rate = divider_round_rate(hw, req->rate,
+ &req->best_parent_rate,
+ cd->div.table,
+ cd->div.width,
+ cd->div.flags);
+
+ req->best_parent_hw = clk_hw_get_parent(hw);
+
+ return 0;
+ }
+
return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
req, ccu_div_round_rate, cd);
}
diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
index 06540f7cf41c..08d074451204 100644
--- a/drivers/clk/sunxi-ng/ccu_div.h
+++ b/drivers/clk/sunxi-ng/ccu_div.h
@@ -41,6 +41,7 @@ struct ccu_div_internal {
u8 width;
u32 max;
+ u32 offset;
u32 flags;
@@ -58,20 +59,27 @@ struct ccu_div_internal {
#define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \
_SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0)
-#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \
+#define _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, _off, _max, _flags) \
{ \
.shift = _shift, \
.width = _width, \
.flags = _flags, \
.max = _max, \
+ .offset = _off, \
}
+#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \
+ _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, 1, _max, _flags)
+
#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \
_SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, 0, _flags)
#define _SUNXI_CCU_DIV_MAX(_shift, _width, _max) \
_SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, 0)
+#define _SUNXI_CCU_DIV_OFFSET(_shift, _width, _offset) \
+ _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, _offset, 0, 0)
+
#define _SUNXI_CCU_DIV(_shift, _width) \
_SUNXI_CCU_DIV_FLAGS(_shift, _width, 0)
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
index ebb1b31568a5..22c2ca7a2a22 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.c
+++ b/drivers/clk/sunxi-ng/ccu_mp.c
@@ -89,11 +89,14 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw,
m = reg >> cmp->m.shift;
m &= (1 << cmp->m.width) - 1;
+ m += cmp->m.offset;
+ if (!m)
+ m++;
p = reg >> cmp->p.shift;
p &= (1 << cmp->p.width) - 1;
- return (parent_rate >> p) / (m + 1);
+ return (parent_rate >> p) / m;
}
static int ccu_mp_determine_rate(struct clk_hw *hw,
@@ -124,9 +127,10 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
reg = readl(cmp->common.base + cmp->common.reg);
reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
+ reg |= (m - cmp->m.offset) << cmp->m.shift;
+ reg |= ilog2(p) << cmp->p.shift;
- writel(reg | (ilog2(p) << cmp->p.shift) | ((m - 1) << cmp->m.shift),
- cmp->common.base + cmp->common.reg);
+ writel(reg, cmp->common.base + cmp->common.reg);
spin_unlock_irqrestore(cmp->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
index 678b6cb49f01..8724c01171b1 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.c
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -40,8 +40,13 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
struct ccu_mult *cm = data;
struct _ccu_mult _cm;
- _cm.min = 1;
- _cm.max = 1 << cm->mult.width;
+ _cm.min = cm->mult.min;
+
+ if (cm->mult.max)
+ _cm.max = cm->mult.max;
+ else
+ _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
+
ccu_mult_find_best(parent_rate, rate, &_cm);
return parent_rate * _cm.mult;
@@ -75,6 +80,9 @@ static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
unsigned long val;
u32 reg;
+ if (ccu_frac_helper_is_enabled(&cm->common, &cm->frac))
+ return ccu_frac_helper_read_rate(&cm->common, &cm->frac);
+
reg = readl(cm->common.base + cm->common.reg);
val = reg >> cm->mult.shift;
val &= (1 << cm->mult.width) - 1;
@@ -82,7 +90,7 @@ static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
&parent_rate);
- return parent_rate * (val + 1);
+ return parent_rate * (val + cm->mult.offset);
}
static int ccu_mult_determine_rate(struct clk_hw *hw,
@@ -102,20 +110,30 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long flags;
u32 reg;
+ if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate))
+ return ccu_frac_helper_set_rate(&cm->common, &cm->frac, rate);
+ else
+ ccu_frac_helper_disable(&cm->common, &cm->frac);
+
ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
&parent_rate);
_cm.min = cm->mult.min;
- _cm.max = 1 << cm->mult.width;
+
+ if (cm->mult.max)
+ _cm.max = cm->mult.max;
+ else
+ _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
+
ccu_mult_find_best(parent_rate, rate, &_cm);
spin_lock_irqsave(cm->common.lock, flags);
reg = readl(cm->common.base + cm->common.reg);
reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
+ reg |= ((_cm.mult - cm->mult.offset) << cm->mult.shift);
- writel(reg | ((_cm.mult - 1) << cm->mult.shift),
- cm->common.base + cm->common.reg);
+ writel(reg, cm->common.base + cm->common.reg);
spin_unlock_irqrestore(cm->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h
index c1a2134bdc71..524acddfcb2e 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.h
+++ b/drivers/clk/sunxi-ng/ccu_mult.h
@@ -2,27 +2,39 @@
#define _CCU_MULT_H_
#include "ccu_common.h"
+#include "ccu_frac.h"
#include "ccu_mux.h"
struct ccu_mult_internal {
+ u8 offset;
u8 shift;
u8 width;
u8 min;
+ u8 max;
};
-#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min) \
- { \
- .shift = _shift, \
- .width = _width, \
- .min = _min, \
+#define _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, _offset, _min, _max) \
+ { \
+ .min = _min, \
+ .max = _max, \
+ .offset = _offset, \
+ .shift = _shift, \
+ .width = _width, \
}
+#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min) \
+ _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, 1, _min, 0)
+
+#define _SUNXI_CCU_MULT_OFFSET(_shift, _width, _offset) \
+ _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, _offset, 1, 0)
+
#define _SUNXI_CCU_MULT(_shift, _width) \
- _SUNXI_CCU_MULT_MIN(_shift, _width, 1)
+ _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, 1, 1, 0)
struct ccu_mult {
u32 enable;
+ struct ccu_frac_internal frac;
struct ccu_mult_internal mult;
struct ccu_mux_internal mux;
struct ccu_common common;
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index a43ad52a957d..c6bb1f523232 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -25,9 +25,15 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
int i;
if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
- (common->features & CCU_FEATURE_VARIABLE_PREDIV)))
+ (common->features & CCU_FEATURE_VARIABLE_PREDIV) ||
+ (common->features & CCU_FEATURE_ALL_PREDIV)))
return;
+ if (common->features & CCU_FEATURE_ALL_PREDIV) {
+ *parent_rate = *parent_rate / common->prediv;
+ return;
+ }
+
reg = readl(common->base + common->reg);
if (parent_index < 0) {
parent_index = reg >> cm->shift;
@@ -64,19 +70,46 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
struct clk_hw *best_parent, *hw = &common->hw;
unsigned int i;
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
+ unsigned long adj_parent_rate;
+
+ best_parent = clk_hw_get_parent(hw);
+ best_parent_rate = clk_hw_get_rate(best_parent);
+
+ adj_parent_rate = best_parent_rate;
+ ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1,
+ &adj_parent_rate);
+
+ best_rate = round(cm, adj_parent_rate, req->rate, data);
+
+ goto out;
+ }
+
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- unsigned long tmp_rate, parent_rate;
+ unsigned long tmp_rate, parent_rate, adj_parent_rate;
struct clk_hw *parent;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
- parent_rate = clk_hw_get_rate(parent);
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
+ struct clk_rate_request parent_req = *req;
+ int ret = __clk_determine_rate(parent, &parent_req);
+
+ if (ret)
+ continue;
+
+ parent_rate = parent_req.rate;
+ } else {
+ parent_rate = clk_hw_get_rate(parent);
+ }
+
+ adj_parent_rate = parent_rate;
ccu_mux_helper_adjust_parent_for_prediv(common, cm, i,
- &parent_rate);
+ &adj_parent_rate);
- tmp_rate = round(cm, clk_hw_get_rate(parent), req->rate, data);
+ tmp_rate = round(cm, adj_parent_rate, req->rate, data);
if (tmp_rate == req->rate) {
best_parent = parent;
best_parent_rate = parent_rate;
diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c
index eaf0fdf78d2b..b9e9b8a9d1b4 100644
--- a/drivers/clk/sunxi-ng/ccu_nk.c
+++ b/drivers/clk/sunxi-ng/ccu_nk.c
@@ -76,12 +76,17 @@ static unsigned long ccu_nk_recalc_rate(struct clk_hw *hw,
n = reg >> nk->n.shift;
n &= (1 << nk->n.width) - 1;
+ n += nk->n.offset;
+ if (!n)
+ n++;
k = reg >> nk->k.shift;
k &= (1 << nk->k.width) - 1;
+ k += nk->k.offset;
+ if (!k)
+ k++;
- rate = parent_rate * (n + 1) * (k + 1);
-
+ rate = parent_rate * n * k;
if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate /= nk->fixed_post_div;
@@ -98,9 +103,9 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate,
rate *= nk->fixed_post_div;
_nk.min_n = nk->n.min;
- _nk.max_n = 1 << nk->n.width;
+ _nk.max_n = nk->n.max ?: 1 << nk->n.width;
_nk.min_k = nk->k.min;
- _nk.max_k = 1 << nk->k.width;
+ _nk.max_k = nk->k.max ?: 1 << nk->k.width;
ccu_nk_find_best(*parent_rate, rate, &_nk);
rate = *parent_rate * _nk.n * _nk.k;
@@ -123,9 +128,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
rate = rate * nk->fixed_post_div;
_nk.min_n = nk->n.min;
- _nk.max_n = 1 << nk->n.width;
+ _nk.max_n = nk->n.max ?: 1 << nk->n.width;
_nk.min_k = nk->k.min;
- _nk.max_k = 1 << nk->k.width;
+ _nk.max_k = nk->k.max ?: 1 << nk->k.width;
ccu_nk_find_best(parent_rate, rate, &_nk);
@@ -135,8 +140,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift);
reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift);
- writel(reg | ((_nk.k - 1) << nk->k.shift) | ((_nk.n - 1) << nk->n.shift),
- nk->common.base + nk->common.reg);
+ reg |= (_nk.k - nk->k.offset) << nk->k.shift;
+ reg |= (_nk.n - nk->n.offset) << nk->n.shift;
+ writel(reg, nk->common.base + nk->common.reg);
spin_unlock_irqrestore(nk->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index 9b840a47a94d..71f81e95a061 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -82,14 +82,23 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
n = reg >> nkm->n.shift;
n &= (1 << nkm->n.width) - 1;
+ n += nkm->n.offset;
+ if (!n)
+ n++;
k = reg >> nkm->k.shift;
k &= (1 << nkm->k.width) - 1;
+ k += nkm->k.offset;
+ if (!k)
+ k++;
m = reg >> nkm->m.shift;
m &= (1 << nkm->m.width) - 1;
+ m += nkm->m.offset;
+ if (!m)
+ m++;
- return parent_rate * (n + 1) * (k + 1) / (m + 1);
+ return parent_rate * n * k / m;
}
static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
@@ -101,9 +110,9 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
struct _ccu_nkm _nkm;
_nkm.min_n = nkm->n.min;
- _nkm.max_n = 1 << nkm->n.width;
+ _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
_nkm.min_k = nkm->k.min;
- _nkm.max_k = 1 << nkm->k.width;
+ _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
_nkm.min_m = 1;
_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
@@ -130,9 +139,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
u32 reg;
_nkm.min_n = nkm->n.min;
- _nkm.max_n = 1 << nkm->n.width;
+ _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
_nkm.min_k = nkm->k.min;
- _nkm.max_k = 1 << nkm->k.width;
+ _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
_nkm.min_m = 1;
_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
@@ -145,10 +154,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift);
reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift);
- reg |= (_nkm.n - 1) << nkm->n.shift;
- reg |= (_nkm.k - 1) << nkm->k.shift;
- reg |= (_nkm.m - 1) << nkm->m.shift;
-
+ reg |= (_nkm.n - nkm->n.offset) << nkm->n.shift;
+ reg |= (_nkm.k - nkm->k.offset) << nkm->k.shift;
+ reg |= (_nkm.m - nkm->m.offset) << nkm->m.shift;
writel(reg, nkm->common.base + nkm->common.reg);
spin_unlock_irqrestore(nkm->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index 684c42da3ebb..a2b40a000157 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -88,17 +88,26 @@ static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw,
n = reg >> nkmp->n.shift;
n &= (1 << nkmp->n.width) - 1;
+ n += nkmp->n.offset;
+ if (!n)
+ n++;
k = reg >> nkmp->k.shift;
k &= (1 << nkmp->k.width) - 1;
+ k += nkmp->k.offset;
+ if (!k)
+ k++;
m = reg >> nkmp->m.shift;
m &= (1 << nkmp->m.width) - 1;
+ m += nkmp->m.offset;
+ if (!m)
+ m++;
p = reg >> nkmp->p.shift;
p &= (1 << nkmp->p.width) - 1;
- return (parent_rate * (n + 1) * (k + 1) >> p) / (m + 1);
+ return parent_rate * n * k >> p / m;
}
static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -108,9 +117,9 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
struct _ccu_nkmp _nkmp;
_nkmp.min_n = nkmp->n.min;
- _nkmp.max_n = 1 << nkmp->n.width;
+ _nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width;
_nkmp.min_k = nkmp->k.min;
- _nkmp.max_k = 1 << nkmp->k.width;
+ _nkmp.max_k = nkmp->k.max ?: 1 << nkmp->k.width;
_nkmp.min_m = 1;
_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
_nkmp.min_p = 1;
@@ -130,9 +139,9 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
u32 reg;
_nkmp.min_n = 1;
- _nkmp.max_n = 1 << nkmp->n.width;
+ _nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width;
_nkmp.min_k = 1;
- _nkmp.max_k = 1 << nkmp->k.width;
+ _nkmp.max_k = nkmp->k.max ?: 1 << nkmp->k.width;
_nkmp.min_m = 1;
_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
_nkmp.min_p = 1;
@@ -148,9 +157,9 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
reg &= ~GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift);
reg &= ~GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift);
- reg |= (_nkmp.n - 1) << nkmp->n.shift;
- reg |= (_nkmp.k - 1) << nkmp->k.shift;
- reg |= (_nkmp.m - 1) << nkmp->m.shift;
+ reg |= (_nkmp.n - nkmp->n.offset) << nkmp->n.shift;
+ reg |= (_nkmp.k - nkmp->k.offset) << nkmp->k.shift;
+ reg |= (_nkmp.m - nkmp->m.offset) << nkmp->m.shift;
reg |= ilog2(_nkmp.p) << nkmp->p.shift;
writel(reg, nkmp->common.base + nkmp->common.reg);
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index c9f3b6c982f0..af71b1909cd9 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -80,11 +80,17 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw,
n = reg >> nm->n.shift;
n &= (1 << nm->n.width) - 1;
+ n += nm->n.offset;
+ if (!n)
+ n++;
m = reg >> nm->m.shift;
m &= (1 << nm->m.width) - 1;
+ m += nm->m.offset;
+ if (!m)
+ m++;
- return parent_rate * (n + 1) / (m + 1);
+ return parent_rate * n / m;
}
static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -94,7 +100,7 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
struct _ccu_nm _nm;
_nm.min_n = nm->n.min;
- _nm.max_n = 1 << nm->n.width;
+ _nm.max_n = nm->n.max ?: 1 << nm->n.width;
_nm.min_m = 1;
_nm.max_m = nm->m.max ?: 1 << nm->m.width;
@@ -117,7 +123,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
ccu_frac_helper_disable(&nm->common, &nm->frac);
_nm.min_n = 1;
- _nm.max_n = 1 << nm->n.width;
+ _nm.max_n = nm->n.max ?: 1 << nm->n.width;
_nm.min_m = 1;
_nm.max_m = nm->m.max ?: 1 << nm->m.width;
@@ -129,8 +135,9 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift);
reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
- writel(reg | ((_nm.m - 1) << nm->m.shift) | ((_nm.n - 1) << nm->n.shift),
- nm->common.base + nm->common.reg);
+ reg |= (_nm.n - nm->n.offset) << nm->n.shift;
+ reg |= (_nm.m - nm->m.offset) << nm->m.shift;
+ writel(reg, nm->common.base + nm->common.reg);
spin_unlock_irqrestore(nm->common.lock, flags);
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index b4e5de16e561..6bb87784a0d6 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -140,6 +140,35 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
return true;
}
+static int _div_round_up(const struct clk_div_table *table,
+ unsigned long parent_rate, unsigned long rate)
+{
+ const struct clk_div_table *clkt;
+ int up = INT_MAX;
+ int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
+
+ for (clkt = table; clkt->div; clkt++) {
+ if (clkt->div == div)
+ return clkt->div;
+ else if (clkt->div < div)
+ continue;
+
+ if ((clkt->div - div) < (up - div))
+ up = clkt->div;
+ }
+
+ return up;
+}
+
+static int _div_round(const struct clk_div_table *table,
+ unsigned long parent_rate, unsigned long rate)
+{
+ if (!table)
+ return DIV_ROUND_UP(parent_rate, rate);
+
+ return _div_round_up(table, parent_rate, rate);
+}
+
static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate)
{
@@ -155,7 +184,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
- bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = _div_round(divider->table, parent_rate, rate);
bestdiv = bestdiv == 0 ? 1 : bestdiv;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c
index 0007218ce6a0..2cf386347f0c 100644
--- a/drivers/clk/uniphier/clk-uniphier-core.c
+++ b/drivers/clk/uniphier/clk-uniphier-core.c
@@ -90,11 +90,8 @@ static int uniphier_clk_probe(struct platform_device *pdev)
dev_dbg(dev, "register %s (index=%d)\n", p->name, p->idx);
hw = uniphier_clk_register(dev, regmap, p);
- if (IS_ERR(hw)) {
- dev_err(dev, "failed to register %s (error %ld)\n",
- p->name, PTR_ERR(hw));
- return PTR_ERR(hw);
- }
+ if (WARN(IS_ERR(hw), "failed to register %s", p->name))
+ continue;
if (p->idx >= 0)
hw_data->hws[p->idx] = hw;
diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c
index 9bff26e0cbb0..ec11f55594ad 100644
--- a/drivers/clk/uniphier/clk-uniphier-cpugear.c
+++ b/drivers/clk/uniphier/clk-uniphier-cpugear.c
@@ -14,7 +14,6 @@
*/
#include <linux/clk-provider.h>
-#include <linux/delay.h>
#include <linux/device.h>
#include <linux/regmap.h>
diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c
index d049316c1c0f..c8027d909429 100644
--- a/drivers/clk/uniphier/clk-uniphier-sys.c
+++ b/drivers/clk/uniphier/clk-uniphier-sys.c
@@ -29,6 +29,15 @@
UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 10), \
UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15)
+#define UNIPHIER_SLD3_SYS_CLK_NAND(idx) \
+ UNIPHIER_CLK_GATE("nand", (idx), NULL, 0x2104, 2)
+
+#define UNIPHIER_LD11_SYS_CLK_NAND(idx) \
+ UNIPHIER_CLK_GATE("nand", (idx), NULL, 0x210c, 0)
+
+#define UNIPHIER_LD11_SYS_CLK_EMMC(idx) \
+ UNIPHIER_CLK_GATE("emmc", (idx), NULL, 0x210c, 2)
+
#define UNIPHIER_SLD3_SYS_CLK_STDMAC(idx) \
UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x2104, 10)
@@ -48,6 +57,7 @@ const struct uniphier_clk_data uniphier_sld3_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16),
+ UNIPHIER_SLD3_SYS_CLK_NAND(2),
UNIPHIER_SLD3_SYS_CLK_SD,
UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
UNIPHIER_SLD3_SYS_CLK_STDMAC(8),
@@ -61,6 +71,7 @@ const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16),
+ UNIPHIER_SLD3_SYS_CLK_NAND(2),
UNIPHIER_SLD3_SYS_CLK_SD,
UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */
@@ -74,6 +85,7 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32),
+ UNIPHIER_SLD3_SYS_CLK_NAND(2),
UNIPHIER_SLD3_SYS_CLK_SD,
UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */
@@ -89,6 +101,7 @@ const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16),
+ UNIPHIER_SLD3_SYS_CLK_NAND(2),
UNIPHIER_SLD3_SYS_CLK_SD,
UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */
@@ -101,6 +114,7 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("dapll2", -1, "ref", 144, 125), /* 2949.12 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "dapll2", 1, 40),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48),
+ UNIPHIER_SLD3_SYS_CLK_NAND(2),
UNIPHIER_PRO5_SYS_CLK_SD,
UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC */
UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */
@@ -113,6 +127,7 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("spll", -1, "ref", 96, 1), /* 2400 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 27),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48),
+ UNIPHIER_SLD3_SYS_CLK_NAND(2),
UNIPHIER_PRO5_SYS_CLK_SD,
UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, RLE */
/* GIO is always clock-enabled: no function for 0x2104 bit6 */
@@ -131,6 +146,9 @@ const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("vspll", -1, "ref", 80, 1), /* 2000 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40),
+ UNIPHIER_LD11_SYS_CLK_NAND(2),
+ UNIPHIER_LD11_SYS_CLK_EMMC(4),
+ /* Index 5 reserved for eMMC PHY */
UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */
UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25),
/* CPU gears */
@@ -156,6 +174,9 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("vppll", -1, "ref", 504, 5), /* 2520 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40),
+ UNIPHIER_LD11_SYS_CLK_NAND(2),
+ UNIPHIER_LD11_SYS_CLK_EMMC(4),
+ /* Index 5 reserved for eMMC PHY */
UNIPHIER_LD20_SYS_CLK_SD,
UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */
/* GIO is always clock-enabled: no function for 0x210c bit5 */
diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c
index a07c31e6f26d..2257d12ba988 100644
--- a/drivers/clk/ux500/abx500-clk.c
+++ b/drivers/clk/ux500/abx500-clk.c
@@ -10,20 +10,26 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
-#include <linux/mfd/dbx500-prcmu.h>
+#include <dt-bindings/clock/ste-ab8500.h>
#include "clk.h"
+#define AB8500_NUM_CLKS 6
+
+static struct clk *ab8500_clks[AB8500_NUM_CLKS];
+static struct clk_onecell_data ab8500_clk_data;
+
/* Clock definitions for ab8500 */
static int ab8500_reg_clks(struct device *dev)
{
int ret;
struct clk *clk;
-
+ struct device_node *np = dev->of_node;
const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"};
u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1};
u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK};
@@ -32,55 +38,52 @@ static int ab8500_reg_clks(struct device *dev)
(1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT)
};
- dev_info(dev, "register clocks for ab850x\n");
-
/* Enable SWAT */
ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
if (ret)
return ret;
- /* ab8500_sysclk */
- clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK, 0);
- clk_register_clkdev(clk, "sysclk", "ab8500-usb.0");
- clk_register_clkdev(clk, "sysclk", "ab-iddet.0");
- clk_register_clkdev(clk, "sysclk", "snd-soc-mop500.0");
- clk_register_clkdev(clk, "sysclk", "shrm_bus");
-
/* ab8500_sysclk2 */
clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk2", "ab8500_sysclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, 0, 0);
- clk_register_clkdev(clk, "sysclk", "0-0070");
+ ab8500_clks[AB8500_SYSCLK_BUF2] = clk;
/* ab8500_sysclk3 */
clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk3", "ab8500_sysclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, 0, 0);
- clk_register_clkdev(clk, "sysclk", "cg1960_core.0");
+ ab8500_clks[AB8500_SYSCLK_BUF3] = clk;
/* ab8500_sysclk4 */
clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0);
+ ab8500_clks[AB8500_SYSCLK_BUF4] = clk;
/* ab_ulpclk */
clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL,
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
38400000, 9000, 0);
- clk_register_clkdev(clk, "ulpclk", "snd-soc-mop500.0");
+ ab8500_clks[AB8500_SYSCLK_ULP] = clk;
/* ab8500_intclk */
clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2,
intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0);
- clk_register_clkdev(clk, "intclk", "snd-soc-mop500.0");
- clk_register_clkdev(clk, NULL, "ab8500-pwm.1");
+ ab8500_clks[AB8500_SYSCLK_INT] = clk;
/* ab8500_audioclk */
clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA,
AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0);
- clk_register_clkdev(clk, "audioclk", "ab8500-codec.0");
+ ab8500_clks[AB8500_SYSCLK_AUDIO] = clk;
+
+ ab8500_clk_data.clks = ab8500_clks;
+ ab8500_clk_data.clk_num = ARRAY_SIZE(ab8500_clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &ab8500_clk_data);
+
+ dev_info(dev, "registered clocks for ab850x\n");
return 0;
}
@@ -116,9 +119,15 @@ static int abx500_clk_probe(struct platform_device *pdev)
return ret;
}
+static const struct of_device_id abx500_clk_match[] = {
+ { .compatible = "stericsson,ab8500-clk", },
+ {}
+};
+
static struct platform_driver abx500_clk_driver = {
.driver = {
.name = "abx500-clk",
+ .of_match_table = abx500_clk_match,
},
.probe = abx500_clk_probe,
};
@@ -127,7 +136,6 @@ static int __init abx500_clk_init(void)
{
return platform_driver_register(&abx500_clk_driver);
}
-
arch_initcall(abx500_clk_init);
MODULE_AUTHOR("Ulf Hansson <ulf.hansson@linaro.org");
diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c
index e960d686d9db..d5888591e1a9 100644
--- a/drivers/clk/ux500/u8500_of_clk.c
+++ b/drivers/clk/ux500/u8500_of_clk.c
@@ -206,6 +206,9 @@ static void u8500_clk_init(struct device_node *np)
clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, 0);
prcmu_clk[PRCMU_TIMCLK] = clk;
+ clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK, 0);
+ prcmu_clk[PRCMU_SYSCLK] = clk;
+
clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, PRCMU_SDMMCCLK,
100000000, CLK_SET_RATE_GATE);
prcmu_clk[PRCMU_SDMMCCLK] = clk;
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
index 04781389d0fb..1367afb03858 100644
--- a/drivers/clk/x86/Makefile
+++ b/drivers/clk/x86/Makefile
@@ -1,2 +1,3 @@
clk-x86-lpss-objs := clk-lpt.o
obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
+obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o
diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c
new file mode 100644
index 000000000000..2b60577703ef
--- /dev/null
+++ b/drivers/clk/x86/clk-pmc-atom.c
@@ -0,0 +1,371 @@
+/*
+ * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Irina Tirdea <irina.tirdea@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/platform_data/x86/clk-pmc-atom.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PLT_CLK_NAME_BASE "pmc_plt_clk"
+
+#define PMC_CLK_CTL_OFFSET 0x60
+#define PMC_CLK_CTL_SIZE 4
+#define PMC_CLK_NUM 6
+#define PMC_CLK_CTL_GATED_ON_D3 0x0
+#define PMC_CLK_CTL_FORCE_ON 0x1
+#define PMC_CLK_CTL_FORCE_OFF 0x2
+#define PMC_CLK_CTL_RESERVED 0x3
+#define PMC_MASK_CLK_CTL GENMASK(1, 0)
+#define PMC_MASK_CLK_FREQ BIT(2)
+#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */
+#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */
+
+struct clk_plt_fixed {
+ struct clk_hw *clk;
+ struct clk_lookup *lookup;
+};
+
+struct clk_plt {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct clk_lookup *lookup;
+ /* protect access to PMC registers */
+ spinlock_t lock;
+};
+
+#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
+
+struct clk_plt_data {
+ struct clk_plt_fixed **parents;
+ u8 nparents;
+ struct clk_plt *clks[PMC_CLK_NUM];
+};
+
+/* Return an index in parent table */
+static inline int plt_reg_to_parent(int reg)
+{
+ switch (reg & PMC_MASK_CLK_FREQ) {
+ default:
+ case PMC_CLK_FREQ_XTAL:
+ return 0;
+ case PMC_CLK_FREQ_PLL:
+ return 1;
+ }
+}
+
+/* Return clk index of parent */
+static inline int plt_parent_to_reg(int index)
+{
+ switch (index) {
+ default:
+ case 0:
+ return PMC_CLK_FREQ_XTAL;
+ case 1:
+ return PMC_CLK_FREQ_PLL;
+ }
+}
+
+/* Abstract status in simpler enabled/disabled value */
+static inline int plt_reg_to_enabled(int reg)
+{
+ switch (reg & PMC_MASK_CLK_CTL) {
+ case PMC_CLK_CTL_GATED_ON_D3:
+ case PMC_CLK_CTL_FORCE_ON:
+ return 1; /* enabled */
+ case PMC_CLK_CTL_FORCE_OFF:
+ case PMC_CLK_CTL_RESERVED:
+ default:
+ return 0; /* disabled */
+ }
+}
+
+static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
+{
+ u32 tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk->lock, flags);
+
+ tmp = readl(clk->reg);
+ tmp = (tmp & ~mask) | (val & mask);
+ writel(tmp, clk->reg);
+
+ spin_unlock_irqrestore(&clk->lock, flags);
+}
+
+static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_plt *clk = to_clk_plt(hw);
+
+ plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
+
+ return 0;
+}
+
+static u8 plt_clk_get_parent(struct clk_hw *hw)
+{
+ struct clk_plt *clk = to_clk_plt(hw);
+ u32 value;
+
+ value = readl(clk->reg);
+
+ return plt_reg_to_parent(value);
+}
+
+static int plt_clk_enable(struct clk_hw *hw)
+{
+ struct clk_plt *clk = to_clk_plt(hw);
+
+ plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
+
+ return 0;
+}
+
+static void plt_clk_disable(struct clk_hw *hw)
+{
+ struct clk_plt *clk = to_clk_plt(hw);
+
+ plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
+}
+
+static int plt_clk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_plt *clk = to_clk_plt(hw);
+ u32 value;
+
+ value = readl(clk->reg);
+
+ return plt_reg_to_enabled(value);
+}
+
+static const struct clk_ops plt_clk_ops = {
+ .enable = plt_clk_enable,
+ .disable = plt_clk_disable,
+ .is_enabled = plt_clk_is_enabled,
+ .get_parent = plt_clk_get_parent,
+ .set_parent = plt_clk_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+
+static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
+ void __iomem *base,
+ const char **parent_names,
+ int num_parents)
+{
+ struct clk_plt *pclk;
+ struct clk_init_data init;
+ int ret;
+
+ pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
+ if (!pclk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
+ init.ops = &plt_clk_ops;
+ init.flags = 0;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ pclk->hw.init = &init;
+ pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
+ spin_lock_init(&pclk->lock);
+
+ ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
+ if (ret) {
+ pclk = ERR_PTR(ret);
+ goto err_free_init;
+ }
+
+ pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
+ if (!pclk->lookup) {
+ pclk = ERR_PTR(-ENOMEM);
+ goto err_free_init;
+ }
+
+err_free_init:
+ kfree(init.name);
+ return pclk;
+}
+
+static void plt_clk_unregister(struct clk_plt *pclk)
+{
+ clkdev_drop(pclk->lookup);
+}
+
+static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
+ const char *name,
+ const char *parent_name,
+ unsigned long fixed_rate)
+{
+ struct clk_plt_fixed *pclk;
+
+ pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
+ if (!pclk)
+ return ERR_PTR(-ENOMEM);
+
+ pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
+ 0, fixed_rate);
+ if (IS_ERR(pclk->clk))
+ return ERR_CAST(pclk->clk);
+
+ pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
+ if (!pclk->lookup) {
+ clk_hw_unregister_fixed_rate(pclk->clk);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return pclk;
+}
+
+static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
+{
+ clkdev_drop(pclk->lookup);
+ clk_hw_unregister_fixed_rate(pclk->clk);
+}
+
+static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
+ unsigned int i)
+{
+ while (i--)
+ plt_clk_unregister_fixed_rate(data->parents[i]);
+}
+
+static void plt_clk_free_parent_names_loop(const char **parent_names,
+ unsigned int i)
+{
+ while (i--)
+ kfree_const(parent_names[i]);
+ kfree(parent_names);
+}
+
+static void plt_clk_unregister_loop(struct clk_plt_data *data,
+ unsigned int i)
+{
+ while (i--)
+ plt_clk_unregister(data->clks[i]);
+}
+
+static const char **plt_clk_register_parents(struct platform_device *pdev,
+ struct clk_plt_data *data,
+ const struct pmc_clk *clks)
+{
+ const char **parent_names;
+ unsigned int i;
+ int err;
+ int nparents = 0;
+
+ data->nparents = 0;
+ while (clks[nparents].name)
+ nparents++;
+
+ data->parents = devm_kcalloc(&pdev->dev, nparents,
+ sizeof(*data->parents), GFP_KERNEL);
+ if (!data->parents)
+ return ERR_PTR(-ENOMEM);
+
+ parent_names = kcalloc(nparents, sizeof(*parent_names),
+ GFP_KERNEL);
+ if (!parent_names)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < nparents; i++) {
+ data->parents[i] =
+ plt_clk_register_fixed_rate(pdev, clks[i].name,
+ clks[i].parent_name,
+ clks[i].freq);
+ if (IS_ERR(data->parents[i])) {
+ err = PTR_ERR(data->parents[i]);
+ goto err_unreg;
+ }
+ parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
+ }
+
+ data->nparents = nparents;
+ return parent_names;
+
+err_unreg:
+ plt_clk_unregister_fixed_rate_loop(data, i);
+ plt_clk_free_parent_names_loop(parent_names, i);
+ return ERR_PTR(err);
+}
+
+static void plt_clk_unregister_parents(struct clk_plt_data *data)
+{
+ plt_clk_unregister_fixed_rate_loop(data, data->nparents);
+}
+
+static int plt_clk_probe(struct platform_device *pdev)
+{
+ const struct pmc_clk_data *pmc_data;
+ const char **parent_names;
+ struct clk_plt_data *data;
+ unsigned int i;
+ int err;
+
+ pmc_data = dev_get_platdata(&pdev->dev);
+ if (!pmc_data || !pmc_data->clks)
+ return -EINVAL;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
+ if (IS_ERR(parent_names))
+ return PTR_ERR(parent_names);
+
+ for (i = 0; i < PMC_CLK_NUM; i++) {
+ data->clks[i] = plt_clk_register(pdev, i, pmc_data->base,
+ parent_names, data->nparents);
+ if (IS_ERR(data->clks[i])) {
+ err = PTR_ERR(data->clks[i]);
+ goto err_unreg_clk_plt;
+ }
+ }
+
+ plt_clk_free_parent_names_loop(parent_names, data->nparents);
+
+ platform_set_drvdata(pdev, data);
+ return 0;
+
+err_unreg_clk_plt:
+ plt_clk_unregister_loop(data, i);
+ plt_clk_unregister_parents(data);
+ plt_clk_free_parent_names_loop(parent_names, data->nparents);
+ return err;
+}
+
+static int plt_clk_remove(struct platform_device *pdev)
+{
+ struct clk_plt_data *data;
+
+ data = platform_get_drvdata(pdev);
+
+ plt_clk_unregister_loop(data, PMC_CLK_NUM);
+ plt_clk_unregister_parents(data);
+ return 0;
+}
+
+static struct platform_driver plt_clk_driver = {
+ .driver = {
+ .name = "clk-pmc-atom",
+ },
+ .probe = plt_clk_probe,
+ .remove = plt_clk_remove,
+};
+builtin_platform_driver(plt_clk_driver);
diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c
index 707d62956e9b..ad5d1dfb3682 100644
--- a/drivers/clk/zte/clk-zx296718.c
+++ b/drivers/clk/zte/clk-zx296718.c
@@ -610,9 +610,12 @@ static int __init top_clocks_init(struct device_node *np)
}
}
- if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &top_hw_onecell_data))
- panic("could not register clk provider\n");
- pr_info("top clk init over, nr:%d\n", TOP_NR_CLKS);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+ &top_hw_onecell_data);
+ if (ret) {
+ pr_err("failed to register top clk provider: %d\n", ret);
+ return ret;
+ }
return 0;
}
@@ -776,9 +779,12 @@ static int __init lsp0_clocks_init(struct device_node *np)
}
}
- if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp0_hw_onecell_data))
- panic("could not register clk provider\n");
- pr_info("lsp0-clk init over:%d\n", LSP0_NR_CLKS);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+ &lsp0_hw_onecell_data);
+ if (ret) {
+ pr_err("failed to register lsp0 clk provider: %d\n", ret);
+ return ret;
+ }
return 0;
}
@@ -881,9 +887,138 @@ static int __init lsp1_clocks_init(struct device_node *np)
}
}
- if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp1_hw_onecell_data))
- panic("could not register clk provider\n");
- pr_info("lsp1-clk init over, nr:%d\n", LSP1_NR_CLKS);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+ &lsp1_hw_onecell_data);
+ if (ret) {
+ pr_err("failed to register lsp1 clk provider: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+PNAME(audio_wclk_common_p) = {
+ "audio_99m",
+ "audio_24m",
+};
+
+PNAME(audio_timer_p) = {
+ "audio_24m",
+ "audio_32k",
+};
+
+static struct zx_clk_mux audio_mux_clk[] = {
+ MUX(0, "i2s0_wclk_mux", audio_wclk_common_p, AUDIO_I2S0_CLK, 0, 1),
+ MUX(0, "i2s1_wclk_mux", audio_wclk_common_p, AUDIO_I2S1_CLK, 0, 1),
+ MUX(0, "i2s2_wclk_mux", audio_wclk_common_p, AUDIO_I2S2_CLK, 0, 1),
+ MUX(0, "i2s3_wclk_mux", audio_wclk_common_p, AUDIO_I2S3_CLK, 0, 1),
+ MUX(0, "i2c0_wclk_mux", audio_wclk_common_p, AUDIO_I2C0_CLK, 0, 1),
+ MUX(0, "spdif0_wclk_mux", audio_wclk_common_p, AUDIO_SPDIF0_CLK, 0, 1),
+ MUX(0, "spdif1_wclk_mux", audio_wclk_common_p, AUDIO_SPDIF1_CLK, 0, 1),
+ MUX(0, "timer_wclk_mux", audio_timer_p, AUDIO_TIMER_CLK, 0, 1),
+};
+
+static struct clk_zx_audio_divider audio_adiv_clk[] = {
+ AUDIO_DIV(0, "i2s0_wclk_div", "i2s0_wclk_mux", AUDIO_I2S0_DIV_CFG1),
+ AUDIO_DIV(0, "i2s1_wclk_div", "i2s1_wclk_mux", AUDIO_I2S1_DIV_CFG1),
+ AUDIO_DIV(0, "i2s2_wclk_div", "i2s2_wclk_mux", AUDIO_I2S2_DIV_CFG1),
+ AUDIO_DIV(0, "i2s3_wclk_div", "i2s3_wclk_mux", AUDIO_I2S3_DIV_CFG1),
+ AUDIO_DIV(0, "spdif0_wclk_div", "spdif0_wclk_mux", AUDIO_SPDIF0_DIV_CFG1),
+ AUDIO_DIV(0, "spdif1_wclk_div", "spdif1_wclk_mux", AUDIO_SPDIF1_DIV_CFG1),
+};
+
+static struct zx_clk_div audio_div_clk[] = {
+ DIV_T(0, "tdm_wclk_div", "audio_16m384", AUDIO_TDM_CLK, 8, 4, 0, common_div_table),
+};
+
+static struct zx_clk_gate audio_gate_clk[] = {
+ GATE(AUDIO_I2S0_WCLK, "i2s0_wclk", "i2s0_wclk_div", AUDIO_I2S0_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_I2S1_WCLK, "i2s1_wclk", "i2s1_wclk_div", AUDIO_I2S1_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_I2S2_WCLK, "i2s2_wclk", "i2s2_wclk_div", AUDIO_I2S2_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_I2S3_WCLK, "i2s3_wclk", "i2s3_wclk_div", AUDIO_I2S3_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_I2C0_WCLK, "i2c0_wclk", "i2c0_wclk_mux", AUDIO_I2C0_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_SPDIF0_WCLK, "spdif0_wclk", "spdif0_wclk_div", AUDIO_SPDIF0_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_SPDIF1_WCLK, "spdif1_wclk", "spdif1_wclk_div", AUDIO_SPDIF1_CLK, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_TDM_WCLK, "tdm_wclk", "tdm_wclk_div", AUDIO_TDM_CLK, 17, CLK_SET_RATE_PARENT, 0),
+ GATE(AUDIO_TS_PCLK, "tempsensor_pclk", "clk49m5", AUDIO_TS_CLK, 1, 0, 0),
+};
+
+static struct clk_hw_onecell_data audio_hw_onecell_data = {
+ .num = AUDIO_NR_CLKS,
+ .hws = {
+ [AUDIO_NR_CLKS - 1] = NULL,
+ },
+};
+
+static int __init audio_clocks_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+ int i, ret;
+
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: Unable to map audio clk base\n", __func__);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(audio_mux_clk); i++) {
+ if (audio_mux_clk[i].id)
+ audio_hw_onecell_data.hws[audio_mux_clk[i].id] =
+ &audio_mux_clk[i].mux.hw;
+
+ audio_mux_clk[i].mux.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &audio_mux_clk[i].mux.hw);
+ if (ret) {
+ pr_warn("audio clk %s init error!\n",
+ audio_mux_clk[i].mux.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(audio_adiv_clk); i++) {
+ if (audio_adiv_clk[i].id)
+ audio_hw_onecell_data.hws[audio_adiv_clk[i].id] =
+ &audio_adiv_clk[i].hw;
+
+ audio_adiv_clk[i].reg_base += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &audio_adiv_clk[i].hw);
+ if (ret) {
+ pr_warn("audio clk %s init error!\n",
+ audio_adiv_clk[i].hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(audio_div_clk); i++) {
+ if (audio_div_clk[i].id)
+ audio_hw_onecell_data.hws[audio_div_clk[i].id] =
+ &audio_div_clk[i].div.hw;
+
+ audio_div_clk[i].div.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &audio_div_clk[i].div.hw);
+ if (ret) {
+ pr_warn("audio clk %s init error!\n",
+ audio_div_clk[i].div.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(audio_gate_clk); i++) {
+ if (audio_gate_clk[i].id)
+ audio_hw_onecell_data.hws[audio_gate_clk[i].id] =
+ &audio_gate_clk[i].gate.hw;
+
+ audio_gate_clk[i].gate.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &audio_gate_clk[i].gate.hw);
+ if (ret) {
+ pr_warn("audio clk %s init error!\n",
+ audio_gate_clk[i].gate.hw.init->name);
+ }
+ }
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+ &audio_hw_onecell_data);
+ if (ret) {
+ pr_err("failed to register audio clk provider: %d\n", ret);
+ return ret;
+ }
return 0;
}
@@ -892,6 +1027,7 @@ static const struct of_device_id zx_clkc_match_table[] = {
{ .compatible = "zte,zx296718-topcrm", .data = &top_clocks_init },
{ .compatible = "zte,zx296718-lsp0crm", .data = &lsp0_clocks_init },
{ .compatible = "zte,zx296718-lsp1crm", .data = &lsp1_clocks_init },
+ { .compatible = "zte,zx296718-audiocrm", .data = &audio_clocks_init },
{ }
};
diff --git a/drivers/clk/zte/clk.c b/drivers/clk/zte/clk.c
index c4c1251bc1e7..878d879b23ff 100644
--- a/drivers/clk/zte/clk.c
+++ b/drivers/clk/zte/clk.c
@@ -9,6 +9,7 @@
#include <linux/clk-provider.h>
#include <linux/err.h>
+#include <linux/gcd.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/slab.h>
@@ -310,3 +311,129 @@ struct clk *clk_register_zx_audio(const char *name,
return clk;
}
+
+#define CLK_AUDIO_DIV_FRAC BIT(0)
+#define CLK_AUDIO_DIV_INT BIT(1)
+#define CLK_AUDIO_DIV_UNCOMMON BIT(1)
+
+#define CLK_AUDIO_DIV_FRAC_NSHIFT 16
+#define CLK_AUDIO_DIV_INT_FRAC_RE BIT(16)
+#define CLK_AUDIO_DIV_INT_FRAC_MAX (0xffff)
+#define CLK_AUDIO_DIV_INT_FRAC_MIN (0x2)
+#define CLK_AUDIO_DIV_INT_INT_SHIFT 24
+#define CLK_AUDIO_DIV_INT_INT_WIDTH 4
+
+struct zx_clk_audio_div_table {
+ unsigned long rate;
+ unsigned int int_reg;
+ unsigned int frac_reg;
+};
+
+#define to_clk_zx_audio_div(_hw) container_of(_hw, struct clk_zx_audio_divider, hw)
+
+static unsigned long audio_calc_rate(struct clk_zx_audio_divider *audio_div,
+ u32 reg_frac, u32 reg_int,
+ unsigned long parent_rate)
+{
+ unsigned long rate, m, n;
+
+ m = reg_frac & 0xffff;
+ n = (reg_frac >> 16) & 0xffff;
+
+ m = (reg_int & 0xffff) * n + m;
+ rate = (parent_rate * n) / m;
+
+ return rate;
+}
+
+static void audio_calc_reg(struct clk_zx_audio_divider *audio_div,
+ struct zx_clk_audio_div_table *div_table,
+ unsigned long rate, unsigned long parent_rate)
+{
+ unsigned int reg_int, reg_frac;
+ unsigned long m, n, div;
+
+ reg_int = parent_rate / rate;
+
+ if (reg_int > CLK_AUDIO_DIV_INT_FRAC_MAX)
+ reg_int = CLK_AUDIO_DIV_INT_FRAC_MAX;
+ else if (reg_int < CLK_AUDIO_DIV_INT_FRAC_MIN)
+ reg_int = 0;
+ m = parent_rate - rate * reg_int;
+ n = rate;
+
+ div = gcd(m, n);
+ m = m / div;
+ n = n / div;
+
+ if ((m >> 16) || (n >> 16)) {
+ if (m > n) {
+ n = n * 0xffff / m;
+ m = 0xffff;
+ } else {
+ m = m * 0xffff / n;
+ n = 0xffff;
+ }
+ }
+ reg_frac = m | (n << 16);
+
+ div_table->rate = parent_rate * n / (reg_int * n + m);
+ div_table->int_reg = reg_int;
+ div_table->frac_reg = reg_frac;
+}
+
+static unsigned long zx_audio_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
+ u32 reg_frac, reg_int;
+
+ reg_frac = readl_relaxed(zx_audio_div->reg_base);
+ reg_int = readl_relaxed(zx_audio_div->reg_base + 0x4);
+
+ return audio_calc_rate(zx_audio_div, reg_frac, reg_int, parent_rate);
+}
+
+static long zx_audio_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
+ struct zx_clk_audio_div_table divt;
+
+ audio_calc_reg(zx_audio_div, &divt, rate, *prate);
+
+ return audio_calc_rate(zx_audio_div, divt.frac_reg, divt.int_reg, *prate);
+}
+
+static int zx_audio_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
+ struct zx_clk_audio_div_table divt;
+ unsigned int val;
+
+ audio_calc_reg(zx_audio_div, &divt, rate, parent_rate);
+ if (divt.rate != rate)
+ pr_debug("the real rate is:%ld", divt.rate);
+
+ writel_relaxed(divt.frac_reg, zx_audio_div->reg_base);
+
+ val = readl_relaxed(zx_audio_div->reg_base + 0x4);
+ val &= ~0xffff;
+ val |= divt.int_reg | CLK_AUDIO_DIV_INT_FRAC_RE;
+ writel_relaxed(val, zx_audio_div->reg_base + 0x4);
+
+ mdelay(1);
+
+ val = readl_relaxed(zx_audio_div->reg_base + 0x4);
+ val &= ~CLK_AUDIO_DIV_INT_FRAC_RE;
+ writel_relaxed(val, zx_audio_div->reg_base + 0x4);
+
+ return 0;
+}
+
+const struct clk_ops zx_audio_div_ops = {
+ .recalc_rate = zx_audio_div_recalc_rate,
+ .round_rate = zx_audio_div_round_rate,
+ .set_rate = zx_audio_div_set_rate,
+};
diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
index 0df3474b2cf3..84a55a3e2bd4 100644
--- a/drivers/clk/zte/clk.h
+++ b/drivers/clk/zte/clk.h
@@ -153,6 +153,25 @@ struct zx_clk_div {
.id = _id, \
}
+struct clk_zx_audio_divider {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ unsigned int rate_count;
+ spinlock_t *lock;
+ u16 id;
+};
+
+#define AUDIO_DIV(_id, _name, _parent, _reg) \
+{ \
+ .reg_base = (void __iomem *) _reg, \
+ .lock = &clk_lock, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &zx_audio_div_ops, \
+ 0), \
+ .id = _id, \
+}
+
struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg_base,
const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
@@ -167,4 +186,6 @@ struct clk *clk_register_zx_audio(const char *name,
unsigned long flags, void __iomem *reg_base);
extern const struct clk_ops zx_pll_ops;
+extern const struct clk_ops zx_audio_div_ops;
+
#endif