aboutsummaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
authorMeizu OpenSource <patchwork@meizu.com>2016-08-15 10:19:42 +0800
committerMeizu OpenSource <patchwork@meizu.com>2016-08-15 10:19:42 +0800
commitd2e1446d81725c351dc73a03b397ce043fb18452 (patch)
tree4dbc616b7f92aea39cd697a9084205ddb805e344 /drivers/clk
first commit
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig86
-rw-r--r--drivers/clk/Makefile44
-rw-r--r--drivers/clk/clk-axi-clkgen.c331
-rw-r--r--drivers/clk/clk-bcm2835.c68
-rw-r--r--drivers/clk/clk-composite.c210
-rw-r--r--drivers/clk/clk-devres.c55
-rw-r--r--drivers/clk/clk-divider.c322
-rw-r--r--drivers/clk/clk-fixed-factor.c134
-rw-r--r--drivers/clk/clk-fixed-rate.c105
-rw-r--r--drivers/clk/clk-gate.c162
-rw-r--r--drivers/clk/clk-highbank.c338
-rw-r--r--drivers/clk/clk-ls1x.c111
-rw-r--r--drivers/clk/clk-max77686.c229
-rw-r--r--drivers/clk/clk-mux.c165
-rw-r--r--drivers/clk/clk-nomadik.c48
-rw-r--r--drivers/clk/clk-prima2.c1126
-rw-r--r--drivers/clk/clk-si5351.c1520
-rw-r--r--drivers/clk/clk-si5351.h155
-rw-r--r--drivers/clk/clk-twl6040.c126
-rw-r--r--drivers/clk/clk-u300.c746
-rw-r--r--drivers/clk/clk-vt8500.c639
-rw-r--r--drivers/clk/clk-wm831x.c414
-rw-r--r--drivers/clk/clk-zynq.c378
-rw-r--r--drivers/clk/clk.c2131
-rw-r--r--drivers/clk/clkdev.c329
-rwxr-xr-xdrivers/clk/mediatek/Makefile2
-rw-r--r--drivers/clk/mediatek/clk-debug.h29
-rw-r--r--drivers/clk/mediatek/clk-gate.c172
-rw-r--r--drivers/clk/mediatek/clk-gate.h48
-rw-r--r--drivers/clk/mediatek/clk-mt6735-pg.c1251
-rw-r--r--drivers/clk/mediatek/clk-mt6735-pg.h24
-rw-r--r--drivers/clk/mediatek/clk-mt6735-pll.c949
-rw-r--r--drivers/clk/mediatek/clk-mt6735-pll.h14
-rw-r--r--drivers/clk/mediatek/clk-mt6735.c1328
-rw-r--r--drivers/clk/mediatek/clk-mtk.c100
-rw-r--r--drivers/clk/mediatek/clk-mtk.h50
-rw-r--r--drivers/clk/mediatek/clk-pll.c63
-rw-r--r--drivers/clk/mediatek/clk-pll.h50
-rw-r--r--drivers/clk/mmp/Makefile9
-rw-r--r--drivers/clk/mmp/clk-apbc.c152
-rw-r--r--drivers/clk/mmp/clk-apmu.c97
-rw-r--r--drivers/clk/mmp/clk-frac.c153
-rw-r--r--drivers/clk/mmp/clk-mmp2.c449
-rw-r--r--drivers/clk/mmp/clk-pxa168.c346
-rw-r--r--drivers/clk/mmp/clk-pxa910.c320
-rw-r--r--drivers/clk/mmp/clk.h35
-rw-r--r--drivers/clk/mvebu/Kconfig8
-rw-r--r--drivers/clk/mvebu/Makefile3
-rw-r--r--drivers/clk/mvebu/clk-core.c675
-rw-r--r--drivers/clk/mvebu/clk-core.h18
-rw-r--r--drivers/clk/mvebu/clk-cpu.c176
-rw-r--r--drivers/clk/mvebu/clk-gating-ctrl.c250
-rw-r--r--drivers/clk/mvebu/clk-gating-ctrl.h22
-rw-r--r--drivers/clk/mvebu/clk.c23
-rw-r--r--drivers/clk/mxs/Makefile8
-rw-r--r--drivers/clk/mxs/clk-div.c110
-rw-r--r--drivers/clk/mxs/clk-frac.c139
-rw-r--r--drivers/clk/mxs/clk-imx23.c175
-rw-r--r--drivers/clk/mxs/clk-imx28.c255
-rw-r--r--drivers/clk/mxs/clk-pll.c116
-rw-r--r--drivers/clk/mxs/clk-ref.c154
-rw-r--r--drivers/clk/mxs/clk-ssp.c62
-rw-r--r--drivers/clk/mxs/clk.c29
-rw-r--r--drivers/clk/mxs/clk.h66
-rw-r--r--drivers/clk/samsung/Makefile8
-rw-r--r--drivers/clk/samsung/clk-exynos4.c1084
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c523
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c138
-rw-r--r--drivers/clk/samsung/clk-pll.c420
-rw-r--r--drivers/clk/samsung/clk-pll.h41
-rw-r--r--drivers/clk/samsung/clk.c320
-rw-r--r--drivers/clk/samsung/clk.h287
-rw-r--r--drivers/clk/socfpga/Makefile1
-rw-r--r--drivers/clk/socfpga/clk.c172
-rw-r--r--drivers/clk/spear/Makefile10
-rw-r--r--drivers/clk/spear/clk-aux-synth.c199
-rw-r--r--drivers/clk/spear/clk-frac-synth.c165
-rw-r--r--drivers/clk/spear/clk-gpt-synth.c154
-rw-r--r--drivers/clk/spear/clk-vco-pll.c363
-rw-r--r--drivers/clk/spear/clk.c39
-rw-r--r--drivers/clk/spear/clk.h134
-rw-r--r--drivers/clk/spear/spear1310_clock.c1121
-rw-r--r--drivers/clk/spear/spear1340_clock.c1011
-rw-r--r--drivers/clk/spear/spear3xx_clock.c648
-rw-r--r--drivers/clk/spear/spear6xx_clock.c340
-rw-r--r--drivers/clk/sunxi/Makefile5
-rw-r--r--drivers/clk/sunxi/clk-factors.c180
-rw-r--r--drivers/clk/sunxi/clk-factors.h27
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c469
-rw-r--r--drivers/clk/tegra/Makefile12
-rw-r--r--drivers/clk/tegra/clk-audio-sync.c87
-rw-r--r--drivers/clk/tegra/clk-divider.c187
-rw-r--r--drivers/clk/tegra/clk-periph-gate.c188
-rw-r--r--drivers/clk/tegra/clk-periph.c222
-rw-r--r--drivers/clk/tegra/clk-pll-out.c123
-rw-r--r--drivers/clk/tegra/clk-pll.c1582
-rw-r--r--drivers/clk/tegra/clk-super.c166
-rw-r--r--drivers/clk/tegra/clk-tegra114.c2085
-rw-r--r--drivers/clk/tegra/clk-tegra20.c1341
-rw-r--r--drivers/clk/tegra/clk-tegra30.c2007
-rw-r--r--drivers/clk/tegra/clk.c97
-rw-r--r--drivers/clk/tegra/clk.h595
-rw-r--r--drivers/clk/ux500/Makefile16
-rw-r--r--drivers/clk/ux500/abx500-clk.c138
-rw-r--r--drivers/clk/ux500/clk-prcc.c163
-rw-r--r--drivers/clk/ux500/clk-prcmu.c350
-rw-r--r--drivers/clk/ux500/clk-sysctrl.c227
-rw-r--r--drivers/clk/ux500/clk.h90
-rw-r--r--drivers/clk/ux500/u8500_clk.c525
-rw-r--r--drivers/clk/ux500/u8540_clk.c21
-rw-r--r--drivers/clk/ux500/u9540_clk.c21
-rw-r--r--drivers/clk/versatile/Makefile7
-rw-r--r--drivers/clk/versatile/clk-icst.c148
-rw-r--r--drivers/clk/versatile/clk-icst.h18
-rw-r--r--drivers/clk/versatile/clk-impd1.c97
-rw-r--r--drivers/clk/versatile/clk-integrator.c84
-rw-r--r--drivers/clk/versatile/clk-realview.c93
-rw-r--r--drivers/clk/versatile/clk-sp810.c188
-rw-r--r--drivers/clk/versatile/clk-vexpress-osc.c147
-rw-r--r--drivers/clk/versatile/clk-vexpress.c86
-rw-r--r--drivers/clk/x86/Makefile2
-rw-r--r--drivers/clk/x86/clk-lpt.c55
122 files changed, 36929 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
new file mode 100644
index 000000000..0357ac446
--- /dev/null
+++ b/drivers/clk/Kconfig
@@ -0,0 +1,86 @@
+
+config CLKDEV_LOOKUP
+ bool
+ select HAVE_CLK
+
+config HAVE_CLK_PREPARE
+ bool
+
+config HAVE_MACH_CLKDEV
+ bool
+
+config COMMON_CLK
+ bool
+ select HAVE_CLK_PREPARE
+ select CLKDEV_LOOKUP
+ ---help---
+ The common clock framework is a single definition of struct
+ clk, useful across many platforms, as well as an
+ implementation of the clock API in include/linux/clk.h.
+ Architectures utilizing the common struct clk should select
+ this option.
+
+menu "Common Clock Framework"
+ depends on COMMON_CLK
+
+config COMMON_CLK_DEBUG
+ bool "DebugFS representation of clock tree"
+ select DEBUG_FS
+ ---help---
+ Creates a directory hierchy in debugfs for visualizing the clk
+ tree structure. Each directory contains read-only members
+ that export information specific to that clk node: clk_rate,
+ clk_flags, clk_prepare_count, clk_enable_count &
+ clk_notifier_count.
+
+config COMMON_CLK_WM831X
+ tristate "Clock driver for WM831x/2x PMICs"
+ depends on MFD_WM831X
+ ---help---
+ Supports the clocking subsystem of the WM831x/2x series of
+ PMICs from Wolfson Microlectronics.
+
+config COMMON_CLK_VERSATILE
+ bool "Clock driver for ARM Reference designs"
+ depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS
+ ---help---
+ Supports clocking on ARM Reference designs:
+ - Integrator/AP and Integrator/CP
+ - RealView PB1176, EB, PB11MP and PBX
+ - Versatile Express
+
+config COMMON_CLK_MAX77686
+ tristate "Clock driver for Maxim 77686 MFD"
+ depends on MFD_MAX77686
+ ---help---
+ This driver supports Maxim 77686 crystal oscillator clock.
+
+config COMMON_CLK_SI5351
+ tristate "Clock driver for SiLabs 5351A/B/C"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
+ select RATIONAL
+ ---help---
+ This driver supports Silicon Labs 5351A/B/C programmable clock
+ generators.
+
+config CLK_TWL6040
+ tristate "External McPDM functional clock from twl6040"
+ depends on TWL6040_CORE
+ ---help---
+ Enable the external functional clock support on OMAP4+ platforms for
+ McPDM. McPDM module is using the external bit clock on the McPDM bus
+ as functional clock.
+
+config COMMON_CLK_AXI_CLKGEN
+ tristate "AXI clkgen driver"
+ depends on ARCH_ZYNQ || MICROBLAZE
+ help
+ ---help---
+ Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
+ FPGAs. It is commonly used in Analog Devices' reference designs.
+
+endmenu
+
+source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
new file mode 100644
index 000000000..74ba9a79c
--- /dev/null
+++ b/drivers/clk/Makefile
@@ -0,0 +1,44 @@
+# common clock types
+obj-$(CONFIG_HAVE_CLK) += clk-devres.o
+obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
+obj-$(CONFIG_COMMON_CLK) += clk.o
+obj-$(CONFIG_COMMON_CLK) += clk-divider.o
+obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
+obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
+obj-$(CONFIG_COMMON_CLK) += clk-gate.o
+obj-$(CONFIG_COMMON_CLK) += clk-mux.o
+obj-$(CONFIG_COMMON_CLK) += clk-composite.o
+
+# SoCs specific
+obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
+obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
+obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
+obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
+obj-$(CONFIG_PLAT_SPEAR) += spear/
+obj-$(CONFIG_ARCH_U300) += clk-u300.o
+obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
+obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
+obj-$(CONFIG_PLAT_ORION) += mvebu/
+ifeq ($(CONFIG_COMMON_CLK), y)
+obj-$(CONFIG_ARCH_MMP) += mmp/
+endif
+obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
+obj-$(CONFIG_ARCH_SUNXI) += sunxi/
+obj-$(CONFIG_ARCH_U8500) += ux500/
+obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
+obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
+obj-$(CONFIG_ARCH_TEGRA) += tegra/
+obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
+ifneq ($(CONFIG_MTK_LEGACY),y)
+obj-$(CONFIG_MEDIATEK_SOLUTION) += mediatek/
+endif
+
+obj-$(CONFIG_X86) += x86/
+
+# Chip specific
+obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
+obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
+obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
+obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
new file mode 100644
index 000000000..813732784
--- /dev/null
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -0,0 +1,331 @@
+/*
+ * AXI clkgen driver
+ *
+ * Copyright 2012-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/err.h>
+
+#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
+#define AXI_CLKGEN_REG_CLK_OUT1 0x08
+#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
+#define AXI_CLKGEN_REG_CLK_DIV 0x10
+#define AXI_CLKGEN_REG_CLK_FB1 0x14
+#define AXI_CLKGEN_REG_CLK_FB2 0x18
+#define AXI_CLKGEN_REG_LOCK1 0x1c
+#define AXI_CLKGEN_REG_LOCK2 0x20
+#define AXI_CLKGEN_REG_LOCK3 0x24
+#define AXI_CLKGEN_REG_FILTER1 0x28
+#define AXI_CLKGEN_REG_FILTER2 0x2c
+
+struct axi_clkgen {
+ void __iomem *base;
+ struct clk_hw clk_hw;
+};
+
+static uint32_t axi_clkgen_lookup_filter(unsigned int m)
+{
+ switch (m) {
+ case 0:
+ return 0x01001990;
+ case 1:
+ return 0x01001190;
+ case 2:
+ return 0x01009890;
+ case 3:
+ return 0x01001890;
+ case 4:
+ return 0x01008890;
+ case 5 ... 8:
+ return 0x01009090;
+ case 9 ... 11:
+ return 0x01000890;
+ case 12:
+ return 0x08009090;
+ case 13 ... 22:
+ return 0x01001090;
+ case 23 ... 36:
+ return 0x01008090;
+ case 37 ... 46:
+ return 0x08001090;
+ default:
+ return 0x08008090;
+ }
+}
+
+static const uint32_t axi_clkgen_lock_table[] = {
+ 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
+ 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
+ 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
+ 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
+ 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
+ 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
+ 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
+ 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
+ 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
+};
+
+static uint32_t axi_clkgen_lookup_lock(unsigned int m)
+{
+ if (m < ARRAY_SIZE(axi_clkgen_lock_table))
+ return axi_clkgen_lock_table[m];
+ return 0x1f1f00fa;
+}
+
+static const unsigned int fpfd_min = 10000;
+static const unsigned int fpfd_max = 300000;
+static const unsigned int fvco_min = 600000;
+static const unsigned int fvco_max = 1200000;
+
+static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
+ unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
+{
+ unsigned long d, d_min, d_max, _d_min, _d_max;
+ unsigned long m, m_min, m_max;
+ unsigned long f, dout, best_f, fvco;
+
+ fin /= 1000;
+ fout /= 1000;
+
+ best_f = ULONG_MAX;
+ *best_d = 0;
+ *best_m = 0;
+ *best_dout = 0;
+
+ d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
+ d_max = min_t(unsigned long, fin / fpfd_min, 80);
+
+ m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
+ m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
+
+ for (m = m_min; m <= m_max; m++) {
+ _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
+ _d_max = min(d_max, fin * m / fvco_min);
+
+ for (d = _d_min; d <= _d_max; d++) {
+ fvco = fin * m / d;
+
+ dout = DIV_ROUND_CLOSEST(fvco, fout);
+ dout = clamp_t(unsigned long, dout, 1, 128);
+ f = fvco / dout;
+ if (abs(f - fout) < abs(best_f - fout)) {
+ best_f = f;
+ *best_d = d;
+ *best_m = m;
+ *best_dout = dout;
+ if (best_f == fout)
+ return;
+ }
+ }
+ }
+}
+
+static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
+ unsigned int *high, unsigned int *edge, unsigned int *nocount)
+{
+ if (divider == 1)
+ *nocount = 1;
+ else
+ *nocount = 0;
+
+ *high = divider / 2;
+ *edge = divider % 2;
+ *low = divider - *high;
+}
+
+static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val)
+{
+ writel(val, axi_clkgen->base + reg);
+}
+
+static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ *val = readl(axi_clkgen->base + reg);
+}
+
+static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
+{
+ return container_of(clk_hw, struct axi_clkgen, clk_hw);
+}
+
+static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+ unsigned int d, m, dout;
+ unsigned int nocount;
+ unsigned int high;
+ unsigned int edge;
+ unsigned int low;
+ uint32_t filter;
+ uint32_t lock;
+
+ if (parent_rate == 0 || rate == 0)
+ return -EINVAL;
+
+ axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
+
+ if (d == 0 || dout == 0 || m == 0)
+ return -EINVAL;
+
+ filter = axi_clkgen_lookup_filter(m - 1);
+ lock = axi_clkgen_lookup_lock(m - 1);
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
+
+ axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
+ (high << 6) | low);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
+ (edge << 7) | (nocount << 6));
+
+ axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
+ (edge << 13) | (nocount << 12) | (high << 6) | low);
+
+ axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
+ (high << 6) | low);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
+ (edge << 7) | (nocount << 6));
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
+ (((lock >> 16) & 0x1f) << 10) | 0x1);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
+ (((lock >> 24) & 0x1f) << 10) | 0x3e9);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
+
+ return 0;
+}
+
+static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned int d, m, dout;
+
+ axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
+
+ if (d == 0 || dout == 0 || m == 0)
+ return -EINVAL;
+
+ return *parent_rate / d * m / dout;
+}
+
+static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
+ unsigned long parent_rate)
+{
+ struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+ unsigned int d, m, dout;
+ unsigned int reg;
+ unsigned long long tmp;
+
+ axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
+ dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
+ d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
+ m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+
+ if (d == 0 || dout == 0)
+ return 0;
+
+ tmp = (unsigned long long)(parent_rate / d) * m;
+ do_div(tmp, dout);
+
+ if (tmp > ULONG_MAX)
+ return ULONG_MAX;
+
+ return tmp;
+}
+
+static const struct clk_ops axi_clkgen_ops = {
+ .recalc_rate = axi_clkgen_recalc_rate,
+ .round_rate = axi_clkgen_round_rate,
+ .set_rate = axi_clkgen_set_rate,
+};
+
+static int axi_clkgen_probe(struct platform_device *pdev)
+{
+ struct axi_clkgen *axi_clkgen;
+ struct clk_init_data init;
+ const char *parent_name;
+ const char *clk_name;
+ struct resource *mem;
+ struct clk *clk;
+
+ axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
+ if (!axi_clkgen)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(axi_clkgen->base))
+ return PTR_ERR(axi_clkgen->base);
+
+ parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
+ if (!parent_name)
+ return -EINVAL;
+
+ clk_name = pdev->dev.of_node->name;
+ of_property_read_string(pdev->dev.of_node, "clock-output-names",
+ &clk_name);
+
+ init.name = clk_name;
+ init.ops = &axi_clkgen_ops;
+ init.flags = 0;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ axi_clkgen->clk_hw.init = &init;
+ clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
+ clk);
+}
+
+static int axi_clkgen_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id axi_clkgen_ids[] = {
+ { .compatible = "adi,axi-clkgen-1.00.a" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
+
+static struct platform_driver axi_clkgen_driver = {
+ .driver = {
+ .name = "adi-axi-clkgen",
+ .owner = THIS_MODULE,
+ .of_match_table = axi_clkgen_ids,
+ },
+ .probe = axi_clkgen_probe,
+ .remove = axi_clkgen_remove,
+};
+module_platform_driver(axi_clkgen_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");
diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c
new file mode 100644
index 000000000..792bc57a9
--- /dev/null
+++ b/drivers/clk/clk-bcm2835.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 Broadcom
+ * Copyright (C) 2012 Stephen Warren
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/bcm2835.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+
+static const __initconst struct of_device_id clk_match[] = {
+ { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
+ { }
+};
+
+/*
+ * These are fixed clocks. They're probably not all root clocks and it may
+ * be possible to turn them on and off but until this is mapped out better
+ * it's the only way they can be used.
+ */
+void __init bcm2835_init_clocks(void)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = clk_register_fixed_rate(NULL, "sys_pclk", NULL, CLK_IS_ROOT,
+ 250000000);
+ if (IS_ERR(clk))
+ pr_err("sys_pclk not registered\n");
+
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT,
+ 126000000);
+ if (IS_ERR(clk))
+ pr_err("apb_pclk not registered\n");
+
+ clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, CLK_IS_ROOT,
+ 3000000);
+ if (IS_ERR(clk))
+ pr_err("uart0_pclk not registered\n");
+ ret = clk_register_clkdev(clk, NULL, "20201000.uart");
+ if (ret)
+ pr_err("uart0_pclk alias not registered\n");
+
+ clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, CLK_IS_ROOT,
+ 125000000);
+ if (IS_ERR(clk))
+ pr_err("uart1_pclk not registered\n");
+ ret = clk_register_clkdev(clk, NULL, "20215000.uart");
+ if (ret)
+ pr_err("uart1_pclk alias not registered\n");
+
+ of_clk_init(clk_match);
+}
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
new file mode 100644
index 000000000..a33f46f20
--- /dev/null
+++ b/drivers/clk/clk-composite.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
+
+static u8 clk_composite_get_parent(struct clk_hw *hw)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *mux_ops = composite->mux_ops;
+ struct clk_hw *mux_hw = composite->mux_hw;
+
+ mux_hw->clk = hw->clk;
+
+ return mux_ops->get_parent(mux_hw);
+}
+
+static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *mux_ops = composite->mux_ops;
+ struct clk_hw *mux_hw = composite->mux_hw;
+
+ mux_hw->clk = hw->clk;
+
+ return mux_ops->set_parent(mux_hw, index);
+}
+
+static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *rate_ops = composite->rate_ops;
+ struct clk_hw *rate_hw = composite->rate_hw;
+
+ rate_hw->clk = hw->clk;
+
+ return rate_ops->recalc_rate(rate_hw, parent_rate);
+}
+
+static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *rate_ops = composite->rate_ops;
+ struct clk_hw *rate_hw = composite->rate_hw;
+
+ rate_hw->clk = hw->clk;
+
+ return rate_ops->round_rate(rate_hw, rate, prate);
+}
+
+static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *rate_ops = composite->rate_ops;
+ struct clk_hw *rate_hw = composite->rate_hw;
+
+ rate_hw->clk = hw->clk;
+
+ return rate_ops->set_rate(rate_hw, rate, parent_rate);
+}
+
+static int clk_composite_is_enabled(struct clk_hw *hw)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *gate_ops = composite->gate_ops;
+ struct clk_hw *gate_hw = composite->gate_hw;
+
+ gate_hw->clk = hw->clk;
+
+ return gate_ops->is_enabled(gate_hw);
+}
+
+static int clk_composite_enable(struct clk_hw *hw)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *gate_ops = composite->gate_ops;
+ struct clk_hw *gate_hw = composite->gate_hw;
+
+ gate_hw->clk = hw->clk;
+
+ return gate_ops->enable(gate_hw);
+}
+
+static void clk_composite_disable(struct clk_hw *hw)
+{
+ struct clk_composite *composite = to_clk_composite(hw);
+ const struct clk_ops *gate_ops = composite->gate_ops;
+ struct clk_hw *gate_hw = composite->gate_hw;
+
+ gate_hw->clk = hw->clk;
+
+ gate_ops->disable(gate_hw);
+}
+
+struct clk *clk_register_composite(struct device *dev, const char *name,
+ const char **parent_names, int num_parents,
+ struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
+ struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
+ struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
+ unsigned long flags)
+{
+ struct clk *clk;
+ struct clk_init_data init;
+ struct clk_composite *composite;
+ struct clk_ops *clk_composite_ops;
+
+ composite = kzalloc(sizeof(*composite), GFP_KERNEL);
+ if (!composite) {
+ pr_err("%s: could not allocate composite clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ clk_composite_ops = &composite->ops;
+
+ if (mux_hw && mux_ops) {
+ if (!mux_ops->get_parent || !mux_ops->set_parent) {
+ clk = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ composite->mux_hw = mux_hw;
+ composite->mux_ops = mux_ops;
+ clk_composite_ops->get_parent = clk_composite_get_parent;
+ clk_composite_ops->set_parent = clk_composite_set_parent;
+ }
+
+ if (rate_hw && rate_ops) {
+ if (!rate_ops->recalc_rate) {
+ clk = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ /* .round_rate is a prerequisite for .set_rate */
+ if (rate_ops->round_rate) {
+ clk_composite_ops->round_rate = clk_composite_round_rate;
+ if (rate_ops->set_rate) {
+ clk_composite_ops->set_rate = clk_composite_set_rate;
+ }
+ } else {
+ WARN(rate_ops->set_rate,
+ "%s: missing round_rate op is required\n",
+ __func__);
+ }
+
+ composite->rate_hw = rate_hw;
+ composite->rate_ops = rate_ops;
+ clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
+ }
+
+ if (gate_hw && gate_ops) {
+ if (!gate_ops->is_enabled || !gate_ops->enable ||
+ !gate_ops->disable) {
+ clk = ERR_PTR(-EINVAL);
+ goto err;
+ }
+
+ composite->gate_hw = gate_hw;
+ composite->gate_ops = gate_ops;
+ clk_composite_ops->is_enabled = clk_composite_is_enabled;
+ clk_composite_ops->enable = clk_composite_enable;
+ clk_composite_ops->disable = clk_composite_disable;
+ }
+
+ init.ops = clk_composite_ops;
+ composite->hw.init = &init;
+
+ clk = clk_register(dev, &composite->hw);
+ if (IS_ERR(clk))
+ goto err;
+
+ if (composite->mux_hw)
+ composite->mux_hw->clk = clk;
+
+ if (composite->rate_hw)
+ composite->rate_hw->clk = clk;
+
+ if (composite->gate_hw)
+ composite->gate_hw->clk = clk;
+
+ return clk;
+
+err:
+ kfree(composite);
+ return clk;
+}
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
new file mode 100644
index 000000000..8f5715488
--- /dev/null
+++ b/drivers/clk/clk-devres.c
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+
+static void devm_clk_release(struct device *dev, void *res)
+{
+ clk_put(*(struct clk **)res);
+}
+
+struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+ struct clk **ptr, *clk;
+
+ ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ clk = clk_get(dev, id);
+ if (!IS_ERR(clk)) {
+ *ptr = clk;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL(devm_clk_get);
+
+static int devm_clk_match(struct device *dev, void *res, void *data)
+{
+ struct clk **c = res;
+ if (!c || !*c) {
+ WARN_ON(!c || !*c);
+ return 0;
+ }
+ return *c == data;
+}
+
+void devm_clk_put(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
+
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL(devm_clk_put);
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
new file mode 100644
index 000000000..2ce22447d
--- /dev/null
+++ b/drivers/clk/clk-divider.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Adjustable divider clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/log2.h>
+
+/*
+ * DOC: basic adjustable divider clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable. clk->rate = parent->rate / divisor
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
+
+#define div_mask(d) ((1 << ((d)->width)) - 1)
+
+static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
+{
+ unsigned int maxdiv = 0;
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div > maxdiv)
+ maxdiv = clkt->div;
+ return maxdiv;
+}
+
+static unsigned int _get_maxdiv(struct clk_divider *divider)
+{
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ return div_mask(divider);
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ return 1 << div_mask(divider);
+ if (divider->table)
+ return _get_table_maxdiv(divider->table);
+ return div_mask(divider) + 1;
+}
+
+static unsigned int _get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
+static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
+{
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ return val;
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ return 1 << val;
+ if (divider->table)
+ return _get_table_div(divider->table, val);
+ return val + 1;
+}
+
+static unsigned int _get_table_val(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return clkt->val;
+ return 0;
+}
+
+static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
+{
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ return div;
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ return __ffs(div);
+ if (divider->table)
+ return _get_table_val(divider->table, div);
+ return div - 1;
+}
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ unsigned int div, val;
+
+ val = readl(divider->reg) >> divider->shift;
+ val &= div_mask(divider);
+
+ div = _get_div(divider, val);
+ if (!div) {
+ WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ __clk_get_name(hw->clk));
+ return parent_rate;
+ }
+
+ return parent_rate / div;
+}
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+static bool _is_valid_table_div(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return true;
+ return false;
+}
+
+static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
+{
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ return is_power_of_2(div);
+ if (divider->table)
+ return _is_valid_table_div(divider->table, div);
+ return true;
+}
+
+static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ int i, bestdiv = 0;
+ unsigned long parent_rate, best = 0, now, maxdiv;
+
+ if (!rate)
+ rate = 1;
+
+ maxdiv = _get_maxdiv(divider);
+
+ if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
+ parent_rate = *best_parent_rate;
+ bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = bestdiv == 0 ? 1 : bestdiv;
+ bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
+ return bestdiv;
+ }
+
+ /*
+ * The maximum divider we can use without overflowing
+ * unsigned long in rate * i below
+ */
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+ for (i = 1; i <= maxdiv; i++) {
+ if (!_is_valid_div(divider, i))
+ continue;
+ parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
+ MULT_ROUND_UP(rate, i));
+ now = parent_rate / i;
+ if (now <= rate && now > best) {
+ bestdiv = i;
+ best = now;
+ *best_parent_rate = parent_rate;
+ }
+ }
+
+ if (!bestdiv) {
+ bestdiv = _get_maxdiv(divider);
+ *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
+ }
+
+ return bestdiv;
+}
+
+static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int div;
+ div = clk_divider_bestdiv(hw, rate, prate);
+
+ return *prate / div;
+}
+
+static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ unsigned int div, value;
+ unsigned long flags = 0;
+ u32 val;
+
+ div = parent_rate / rate;
+ value = _get_val(divider, div);
+
+ if (value > div_mask(divider))
+ value = div_mask(divider);
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ val |= value << divider->shift;
+ writel(val, divider->reg);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return 0;
+}
+
+const struct clk_ops clk_divider_ops = {
+ .recalc_rate = clk_divider_recalc_rate,
+ .round_rate = clk_divider_round_rate,
+ .set_rate = clk_divider_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_ops);
+
+static struct clk *_register_divider(struct device *dev, 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,
+ spinlock_t *lock)
+{
+ struct clk_divider *div;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the divider */
+ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+ if (!div) {
+ pr_err("%s: could not allocate divider clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &clk_divider_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* struct clk_divider assignments */
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->flags = clk_divider_flags;
+ div->lock = lock;
+ div->hw.init = &init;
+ div->table = table;
+
+ /* register the clock */
+ clk = clk_register(dev, &div->hw);
+
+ if (IS_ERR(clk))
+ kfree(div);
+
+ return clk;
+}
+
+/**
+ * clk_register_divider - register a divider clock with the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust divider
+ * @shift: number of bits to shift the bitfield
+ * @width: width of the bitfield
+ * @clk_divider_flags: divider-specific flags for this clock
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
+{
+ return _register_divider(dev, name, parent_name, flags, reg, shift,
+ width, clk_divider_flags, NULL, lock);
+}
+
+/**
+ * clk_register_divider_table - register a table based divider clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust divider
+ * @shift: number of bits to shift the bitfield
+ * @width: width of the bitfield
+ * @clk_divider_flags: divider-specific flags for this clock
+ * @table: array of divider/value pairs ending with a div set to 0
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_divider_table(struct device *dev, 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,
+ spinlock_t *lock)
+{
+ return _register_divider(dev, name, parent_name, flags, reg, shift,
+ width, clk_divider_flags, table, lock);
+}
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
new file mode 100644
index 000000000..9ff7d510f
--- /dev/null
+++ b/drivers/clk/clk-fixed-factor.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ * 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.
+ *
+ * Standard functionality for the common clock API.
+ */
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+
+/*
+ * DOC: basic fixed multiplier and divider clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is fixed. clk->rate = parent->rate / div * mult
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_fixed_factor(_hw) container_of(_hw, struct clk_fixed_factor, hw)
+
+static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
+ unsigned long long int rate;
+
+ rate = (unsigned long long int)parent_rate * fix->mult;
+ do_div(rate, fix->div);
+ return (unsigned long)rate;
+}
+
+static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
+
+ if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
+ unsigned long best_parent;
+
+ best_parent = (rate / fix->mult) * fix->div;
+ *prate = __clk_round_rate(__clk_get_parent(hw->clk),
+ best_parent);
+ }
+
+ return (*prate / fix->div) * fix->mult;
+}
+
+static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return 0;
+}
+
+struct clk_ops clk_fixed_factor_ops = {
+ .round_rate = clk_factor_round_rate,
+ .set_rate = clk_factor_set_rate,
+ .recalc_rate = clk_factor_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned int mult, unsigned int div)
+{
+ struct clk_fixed_factor *fix;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ fix = kmalloc(sizeof(*fix), GFP_KERNEL);
+ if (!fix) {
+ pr_err("%s: could not allocate fixed factor clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_fixed_factor assignments */
+ fix->mult = mult;
+ fix->div = div;
+ fix->hw.init = &init;
+
+ init.name = name;
+ init.ops = &clk_fixed_factor_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(dev, &fix->hw);
+
+ if (IS_ERR(clk))
+ kfree(fix);
+
+ return clk;
+}
+#ifdef CONFIG_OF
+/**
+ * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
+ */
+void __init of_fixed_factor_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ u32 div, mult;
+
+ if (of_property_read_u32(node, "clock-div", &div)) {
+ pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
+ __func__, node->name);
+ return;
+ }
+
+ if (of_property_read_u32(node, "clock-mult", &mult)) {
+ pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n",
+ __func__, node->name);
+ return;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0,
+ mult, div);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup);
+CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
+ of_fixed_factor_clk_setup);
+#endif
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
new file mode 100644
index 000000000..dc58fbd85
--- /dev/null
+++ b/drivers/clk/clk-fixed-rate.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Fixed rate clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/of.h>
+
+/*
+ * DOC: basic fixed-rate clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parents are prepared
+ * enable - clk_enable only ensures parents are enabled
+ * rate - rate is always a fixed value. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)
+
+static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return to_clk_fixed_rate(hw)->fixed_rate;
+}
+
+const struct clk_ops clk_fixed_rate_ops = {
+ .recalc_rate = clk_fixed_rate_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
+
+/**
+ * clk_register_fixed_rate - register fixed-rate clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @fixed_rate: non-adjustable clock rate
+ */
+struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned long fixed_rate)
+{
+ struct clk_fixed_rate *fixed;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate fixed-rate clock */
+ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+ if (!fixed) {
+ pr_err("%s: could not allocate fixed clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &clk_fixed_rate_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* struct clk_fixed_rate assignments */
+ fixed->fixed_rate = fixed_rate;
+ fixed->hw.init = &init;
+
+ /* register the clock */
+ clk = clk_register(dev, &fixed->hw);
+
+ if (IS_ERR(clk))
+ kfree(fixed);
+
+ return clk;
+}
+
+#ifdef CONFIG_OF
+/**
+ * of_fixed_clk_setup() - Setup function for simple fixed rate clock
+ */
+void of_fixed_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ u32 rate;
+
+ if (of_property_read_u32(node, "clock-frequency", &rate))
+ return;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
+CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
+#endif
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
new file mode 100644
index 000000000..91be356c2
--- /dev/null
+++ b/drivers/clk/clk-gate.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/**
+ * DOC: basic gatable clock which can gate and ungate it's ouput
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - inherits rate from parent. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+
+/*
+ * It works on following logic:
+ *
+ * For enabling clock, enable = 1
+ * set2dis = 1 -> clear bit -> set = 0
+ * set2dis = 0 -> set bit -> set = 1
+ *
+ * For disabling clock, enable = 0
+ * set2dis = 1 -> set bit -> set = 1
+ * set2dis = 0 -> clear bit -> set = 0
+ *
+ * So, result is always: enable xor set2dis.
+ */
+static void clk_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
+ unsigned long flags = 0;
+ u32 reg;
+
+ set ^= enable;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+
+ reg = readl(gate->reg);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ else
+ reg &= ~BIT(gate->bit_idx);
+
+ writel(reg, gate->reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int clk_gate_enable(struct clk_hw *hw)
+{
+#if !defined(CONFIG_MTK_LEGACY) /* FIXME: only for bring up */
+ printk("[CCF] %s: %s\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif /* !defined(CONFIG_MTK_LEGACY) */
+ clk_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+static void clk_gate_disable(struct clk_hw *hw)
+{
+#if !defined(CONFIG_MTK_LEGACY) /* FIXME: only for bring up */
+ printk("[CCF] %s: %s\n", __func__, __clk_get_name(hw->clk));
+ return;
+#endif /* !defined(CONFIG_MTK_LEGACY) */
+ clk_gate_endisable(hw, 0);
+}
+
+static int clk_gate_is_enabled(struct clk_hw *hw)
+{
+ u32 reg;
+ struct clk_gate *gate = to_clk_gate(hw);
+#if !defined(CONFIG_MTK_LEGACY) /* FIXME: only for bring up */
+ printk("[CCF] %s: %s\n", __func__, __clk_get_name(hw->clk));
+ return 1;
+#endif /* !defined(CONFIG_MTK_LEGACY) */
+
+ reg = readl(gate->reg);
+
+ /* if a set bit disables this clk, flip it before masking */
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ reg ^= BIT(gate->bit_idx);
+
+ reg &= BIT(gate->bit_idx);
+
+ return reg ? 1 : 0;
+}
+
+const struct clk_ops clk_gate_ops = {
+ .enable = clk_gate_enable,
+ .disable = clk_gate_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gate_ops);
+
+/**
+ * clk_register_gate - register a gate clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of this clock's parent
+ * @flags: framework-specific flags for this clock
+ * @reg: register address to control gating of this clock
+ * @bit_idx: which bit in the register controls gating of this clock
+ * @clk_gate_flags: gate-specific flags for this clock
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
+{
+ struct clk_gate *gate;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the gate */
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate) {
+ pr_err("%s: could not allocate gated clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &clk_gate_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* struct clk_gate assignments */
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ gate->flags = clk_gate_flags;
+ gate->lock = lock;
+ gate->hw.init = &init;
+
+ clk = clk_register(dev, &gate->hw);
+
+ if (IS_ERR(clk))
+ kfree(gate);
+
+ return clk;
+}
diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c
new file mode 100644
index 000000000..2e08cb001
--- /dev/null
+++ b/drivers/clk/clk-highbank.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+extern void __iomem *sregs_base;
+
+#define HB_PLL_LOCK_500 0x20000000
+#define HB_PLL_LOCK 0x10000000
+#define HB_PLL_DIVF_SHIFT 20
+#define HB_PLL_DIVF_MASK 0x0ff00000
+#define HB_PLL_DIVQ_SHIFT 16
+#define HB_PLL_DIVQ_MASK 0x00070000
+#define HB_PLL_DIVR_SHIFT 8
+#define HB_PLL_DIVR_MASK 0x00001f00
+#define HB_PLL_RANGE_SHIFT 4
+#define HB_PLL_RANGE_MASK 0x00000070
+#define HB_PLL_BYPASS 0x00000008
+#define HB_PLL_RESET 0x00000004
+#define HB_PLL_EXT_BYPASS 0x00000002
+#define HB_PLL_EXT_ENA 0x00000001
+
+#define HB_PLL_VCO_MIN_FREQ 2133000000
+#define HB_PLL_MAX_FREQ HB_PLL_VCO_MIN_FREQ
+#define HB_PLL_MIN_FREQ (HB_PLL_VCO_MIN_FREQ / 64)
+
+#define HB_A9_BCLK_DIV_MASK 0x00000006
+#define HB_A9_BCLK_DIV_SHIFT 1
+#define HB_A9_PCLK_DIV 0x00000001
+
+struct hb_clk {
+ struct clk_hw hw;
+ void __iomem *reg;
+ char *parent_name;
+};
+#define to_hb_clk(p) container_of(p, struct hb_clk, hw)
+
+static int clk_pll_prepare(struct clk_hw *hwclk)
+ {
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 reg;
+
+ reg = readl(hbclk->reg);
+ reg &= ~HB_PLL_RESET;
+ writel(reg, hbclk->reg);
+
+ while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
+ ;
+ while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
+ ;
+
+ return 0;
+}
+
+static void clk_pll_unprepare(struct clk_hw *hwclk)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 reg;
+
+ reg = readl(hbclk->reg);
+ reg |= HB_PLL_RESET;
+ writel(reg, hbclk->reg);
+}
+
+static int clk_pll_enable(struct clk_hw *hwclk)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 reg;
+
+ reg = readl(hbclk->reg);
+ reg |= HB_PLL_EXT_ENA;
+ writel(reg, hbclk->reg);
+
+ return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hwclk)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 reg;
+
+ reg = readl(hbclk->reg);
+ reg &= ~HB_PLL_EXT_ENA;
+ writel(reg, hbclk->reg);
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ unsigned long divf, divq, vco_freq, reg;
+
+ reg = readl(hbclk->reg);
+ if (reg & HB_PLL_EXT_BYPASS)
+ return parent_rate;
+
+ divf = (reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT;
+ divq = (reg & HB_PLL_DIVQ_MASK) >> HB_PLL_DIVQ_SHIFT;
+ vco_freq = parent_rate * (divf + 1);
+
+ return vco_freq / (1 << divq);
+}
+
+static void clk_pll_calc(unsigned long rate, unsigned long ref_freq,
+ u32 *pdivq, u32 *pdivf)
+{
+ u32 divq, divf;
+ unsigned long vco_freq;
+
+ if (rate < HB_PLL_MIN_FREQ)
+ rate = HB_PLL_MIN_FREQ;
+ if (rate > HB_PLL_MAX_FREQ)
+ rate = HB_PLL_MAX_FREQ;
+
+ for (divq = 1; divq <= 6; divq++) {
+ if ((rate * (1 << divq)) >= HB_PLL_VCO_MIN_FREQ)
+ break;
+ }
+
+ vco_freq = rate * (1 << divq);
+ divf = (vco_freq + (ref_freq / 2)) / ref_freq;
+ divf--;
+
+ *pdivq = divq;
+ *pdivf = divf;
+}
+
+static long clk_pll_round_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ u32 divq, divf;
+ unsigned long ref_freq = *parent_rate;
+
+ clk_pll_calc(rate, ref_freq, &divq, &divf);
+
+ return (ref_freq * (divf + 1)) / (1 << divq);
+}
+
+static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 divq, divf;
+ u32 reg;
+
+ clk_pll_calc(rate, parent_rate, &divq, &divf);
+
+ reg = readl(hbclk->reg);
+ if (divf != ((reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT)) {
+ /* Need to re-lock PLL, so put it into bypass mode */
+ reg |= HB_PLL_EXT_BYPASS;
+ writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
+
+ writel(reg | HB_PLL_RESET, hbclk->reg);
+ reg &= ~(HB_PLL_DIVF_MASK | HB_PLL_DIVQ_MASK);
+ reg |= (divf << HB_PLL_DIVF_SHIFT) | (divq << HB_PLL_DIVQ_SHIFT);
+ writel(reg | HB_PLL_RESET, hbclk->reg);
+ writel(reg, hbclk->reg);
+
+ while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
+ ;
+ while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
+ ;
+ reg |= HB_PLL_EXT_ENA;
+ reg &= ~HB_PLL_EXT_BYPASS;
+ } else {
+ writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
+ reg &= ~HB_PLL_DIVQ_MASK;
+ reg |= divq << HB_PLL_DIVQ_SHIFT;
+ writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
+ }
+ writel(reg, hbclk->reg);
+
+ return 0;
+}
+
+static const struct clk_ops clk_pll_ops = {
+ .prepare = clk_pll_prepare,
+ .unprepare = clk_pll_unprepare,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_pll_set_rate,
+};
+
+static unsigned long clk_cpu_periphclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 div = (readl(hbclk->reg) & HB_A9_PCLK_DIV) ? 8 : 4;
+ return parent_rate / div;
+}
+
+static const struct clk_ops a9periphclk_ops = {
+ .recalc_rate = clk_cpu_periphclk_recalc_rate,
+};
+
+static unsigned long clk_cpu_a9bclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 div = (readl(hbclk->reg) & HB_A9_BCLK_DIV_MASK) >> HB_A9_BCLK_DIV_SHIFT;
+
+ return parent_rate / (div + 2);
+}
+
+static const struct clk_ops a9bclk_ops = {
+ .recalc_rate = clk_cpu_a9bclk_recalc_rate,
+};
+
+static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 div;
+
+ div = readl(hbclk->reg) & 0x1f;
+ div++;
+ div *= 2;
+
+ return parent_rate / div;
+}
+
+static long clk_periclk_round_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ u32 div;
+
+ div = *parent_rate / rate;
+ div++;
+ div &= ~0x1;
+
+ return *parent_rate / div;
+}
+
+static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct hb_clk *hbclk = to_hb_clk(hwclk);
+ u32 div;
+
+ div = parent_rate / rate;
+ if (div & 0x1)
+ return -EINVAL;
+
+ writel(div >> 1, hbclk->reg);
+ return 0;
+}
+
+static const struct clk_ops periclk_ops = {
+ .recalc_rate = clk_periclk_recalc_rate,
+ .round_rate = clk_periclk_round_rate,
+ .set_rate = clk_periclk_set_rate,
+};
+
+static __init struct clk *hb_clk_init(struct device_node *node, const struct clk_ops *ops)
+{
+ u32 reg;
+ struct clk *clk;
+ struct hb_clk *hb_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+
+ rc = of_property_read_u32(node, "reg", &reg);
+ if (WARN_ON(rc))
+ return NULL;
+
+ hb_clk = kzalloc(sizeof(*hb_clk), GFP_KERNEL);
+ if (WARN_ON(!hb_clk))
+ return NULL;
+
+ hb_clk->reg = sregs_base + reg;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ hb_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &hb_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(hb_clk);
+ return NULL;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return clk;
+}
+
+static void __init hb_pll_init(struct device_node *node)
+{
+ hb_clk_init(node, &clk_pll_ops);
+}
+CLK_OF_DECLARE(hb_pll, "calxeda,hb-pll-clock", hb_pll_init);
+
+static void __init hb_a9periph_init(struct device_node *node)
+{
+ hb_clk_init(node, &a9periphclk_ops);
+}
+CLK_OF_DECLARE(hb_a9periph, "calxeda,hb-a9periph-clock", hb_a9periph_init);
+
+static void __init hb_a9bus_init(struct device_node *node)
+{
+ struct clk *clk = hb_clk_init(node, &a9bclk_ops);
+ clk_prepare_enable(clk);
+}
+CLK_OF_DECLARE(hb_a9bus, "calxeda,hb-a9bus-clock", hb_a9bus_init);
+
+static void __init hb_emmc_init(struct device_node *node)
+{
+ hb_clk_init(node, &periclk_ops);
+}
+CLK_OF_DECLARE(hb_emmc, "calxeda,hb-emmc-clock", hb_emmc_init);
diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c
new file mode 100644
index 000000000..f20b75023
--- /dev/null
+++ b/drivers/clk/clk-ls1x.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@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.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <loongson1.h>
+
+#define OSC 33
+
+static DEFINE_SPINLOCK(_lock);
+
+static int ls1x_pll_clk_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+
+static void ls1x_pll_clk_disable(struct clk_hw *hw)
+{
+}
+
+static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u32 pll, rate;
+
+ pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+ rate = ((12 + (pll & 0x3f)) * 1000000) +
+ ((((pll >> 8) & 0x3ff) * 1000000) >> 10);
+ rate *= OSC;
+ rate >>= 1;
+
+ return rate;
+}
+
+static const struct clk_ops ls1x_pll_clk_ops = {
+ .enable = ls1x_pll_clk_enable,
+ .disable = ls1x_pll_clk_disable,
+ .recalc_rate = ls1x_pll_recalc_rate,
+};
+
+static struct clk * __init clk_register_pll(struct device *dev,
+ const char *name, const char *parent_name, unsigned long flags)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the divider */
+ hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
+ if (!hw) {
+ pr_err("%s: could not allocate clk_hw\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &ls1x_pll_clk_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ hw->init = &init;
+
+ /* register the clock */
+ clk = clk_register(dev, hw);
+
+ if (IS_ERR(clk))
+ kfree(hw);
+
+ return clk;
+}
+
+void __init ls1x_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_pll(NULL, "pll_clk", NULL, CLK_IS_ROOT);
+ clk_prepare_enable(clk);
+
+ clk = clk_register_divider(NULL, "cpu_clk", "pll_clk",
+ CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_CPU_SHIFT,
+ DIV_CPU_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "cpu", NULL);
+
+ clk = clk_register_divider(NULL, "dc_clk", "pll_clk",
+ CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
+ DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "dc", NULL);
+
+ clk = clk_register_divider(NULL, "ahb_clk", "pll_clk",
+ CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
+ DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "ahb", NULL);
+ clk_register_clkdev(clk, "stmmaceth", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, 2);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "apb", NULL);
+ clk_register_clkdev(clk, "serial8250", NULL);
+}
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
new file mode 100644
index 000000000..9f57bc37c
--- /dev/null
+++ b/drivers/clk/clk-max77686.c
@@ -0,0 +1,229 @@
+/*
+ * clk-max77686.c - Clock driver for Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * Jonghwa Lee <jonghwa3.lee@samsung.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+enum {
+ MAX77686_CLK_AP = 0,
+ MAX77686_CLK_CP,
+ MAX77686_CLK_PMIC,
+ MAX77686_CLKS_NUM,
+};
+
+struct max77686_clk {
+ struct max77686_dev *iodev;
+ u32 mask;
+ struct clk_hw hw;
+ struct clk_lookup *lookup;
+};
+
+static struct max77686_clk *to_max77686_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct max77686_clk, hw);
+}
+
+static int max77686_clk_prepare(struct clk_hw *hw)
+{
+ struct max77686_clk *max77686 = to_max77686_clk(hw);
+
+ return regmap_update_bits(max77686->iodev->regmap,
+ MAX77686_REG_32KHZ, max77686->mask,
+ max77686->mask);
+}
+
+static void max77686_clk_unprepare(struct clk_hw *hw)
+{
+ struct max77686_clk *max77686 = to_max77686_clk(hw);
+
+ regmap_update_bits(max77686->iodev->regmap,
+ MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
+}
+
+static int max77686_clk_is_enabled(struct clk_hw *hw)
+{
+ struct max77686_clk *max77686 = to_max77686_clk(hw);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(max77686->iodev->regmap,
+ MAX77686_REG_32KHZ, &val);
+
+ if (ret < 0)
+ return -EINVAL;
+
+ return val & max77686->mask;
+}
+
+static struct clk_ops max77686_clk_ops = {
+ .prepare = max77686_clk_prepare,
+ .unprepare = max77686_clk_unprepare,
+ .is_enabled = max77686_clk_is_enabled,
+};
+
+static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
+ [MAX77686_CLK_AP] = {
+ .name = "32khz_ap",
+ .ops = &max77686_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [MAX77686_CLK_CP] = {
+ .name = "32khz_cp",
+ .ops = &max77686_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [MAX77686_CLK_PMIC] = {
+ .name = "32khz_pmic",
+ .ops = &max77686_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+};
+
+static int max77686_clk_register(struct device *dev,
+ struct max77686_clk *max77686)
+{
+ struct clk *clk;
+ struct clk_hw *hw = &max77686->hw;
+
+ clk = clk_register(dev, hw);
+
+ if (IS_ERR(clk))
+ return -ENOMEM;
+
+ max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
+ if (!max77686->lookup)
+ return -ENOMEM;
+
+ max77686->lookup->con_id = hw->init->name;
+ max77686->lookup->clk = clk;
+
+ clkdev_add(max77686->lookup);
+
+ return 0;
+}
+
+static int max77686_clk_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max77686_clk **max77686_clks;
+ int i, ret;
+
+ max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
+ * MAX77686_CLKS_NUM, GFP_KERNEL);
+ if (!max77686_clks)
+ return -ENOMEM;
+
+ for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+ max77686_clks[i] = devm_kzalloc(&pdev->dev,
+ sizeof(struct max77686_clk), GFP_KERNEL);
+ if (!max77686_clks[i])
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+ max77686_clks[i]->iodev = iodev;
+ max77686_clks[i]->mask = 1 << i;
+ max77686_clks[i]->hw.init = &max77686_clks_init[i];
+
+ ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
+ if (ret) {
+ switch (i) {
+ case MAX77686_CLK_AP:
+ dev_err(&pdev->dev, "Fail to register CLK_AP\n");
+ goto err_clk_ap;
+ break;
+ case MAX77686_CLK_CP:
+ dev_err(&pdev->dev, "Fail to register CLK_CP\n");
+ goto err_clk_cp;
+ break;
+ case MAX77686_CLK_PMIC:
+ dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
+ goto err_clk_pmic;
+ }
+ }
+ }
+
+ platform_set_drvdata(pdev, max77686_clks);
+
+ goto out;
+
+err_clk_pmic:
+ clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
+ kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
+err_clk_cp:
+ clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
+ kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
+err_clk_ap:
+out:
+ return ret;
+}
+
+static int max77686_clk_remove(struct platform_device *pdev)
+{
+ struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+ clkdev_drop(max77686_clks[i]->lookup);
+ kfree(max77686_clks[i]->hw.clk);
+ }
+ return 0;
+}
+
+static const struct platform_device_id max77686_clk_id[] = {
+ { "max77686-clk", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, max77686_clk_id);
+
+static struct platform_driver max77686_clk_driver = {
+ .driver = {
+ .name = "max77686-clk",
+ .owner = THIS_MODULE,
+ },
+ .probe = max77686_clk_probe,
+ .remove = max77686_clk_remove,
+ .id_table = max77686_clk_id,
+};
+
+static int __init max77686_clk_init(void)
+{
+ return platform_driver_register(&max77686_clk_driver);
+}
+subsys_initcall(max77686_clk_init);
+
+static void __init max77686_clk_cleanup(void)
+{
+ platform_driver_unregister(&max77686_clk_driver);
+}
+module_exit(max77686_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
+MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
new file mode 100644
index 000000000..533bf73b4
--- /dev/null
+++ b/drivers/clk/clk-mux.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Simple multiplexer clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic adjustable multiplexer clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is only affected by parent switching. No clk_set_rate support
+ * parent - parent is adjustable through clk_set_parent
+ */
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static u8 clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+ u32 val;
+
+#if !defined(CONFIG_MTK_LEGACY) /* FIXME: only for bring up */
+ printk("[CCF] %s: %s, mux=%s\n", __func__, __clk_get_name(hw->clk),
+ __clk_get_name(mux->hw.clk));
+ return 1;
+#endif /* !defined(CONFIG_MTK_LEGACY) */
+ /*
+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+ * to 0x7 (index starts at one)
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+ * val = 0x4 really means "bit 2, index starts at bit 0"
+ */
+ val = readl(mux->reg) >> mux->shift;
+ val &= mux->mask;
+
+ if (mux->table) {
+ int i;
+
+ for (i = 0; i < num_parents; i++)
+ if (mux->table[i] == val)
+ return i;
+ return -EINVAL;
+ }
+
+ if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+ if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= num_parents)
+ return -EINVAL;
+
+ return val;
+}
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+ unsigned long flags = 0;
+
+#if !defined(CONFIG_MTK_LEGACY) /* FIXME: only for bring up */
+ printk("[CCF] %s: %s, mux=%s, index=%u\n", __func__,
+ __clk_get_name(hw->clk), __clk_get_name(mux->hw.clk), index);
+ return 0;
+#endif /* !defined(CONFIG_MTK_LEGACY) */
+ if (mux->table)
+ index = mux->table[index];
+
+ else {
+ if (mux->flags & CLK_MUX_INDEX_BIT)
+ index = (1 << ffs(index));
+
+ if (mux->flags & CLK_MUX_INDEX_ONE)
+ index++;
+ }
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+ val = readl(mux->reg);
+ val &= ~(mux->mask << mux->shift);
+ val |= index << mux->shift;
+ writel(val, mux->reg);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return 0;
+}
+
+const struct clk_ops clk_mux_ops = {
+ .get_parent = clk_mux_get_parent,
+ .set_parent = clk_mux_set_parent,
+};
+EXPORT_SYMBOL_GPL(clk_mux_ops);
+
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ const char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+{
+ struct clk_mux *mux;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the mux */
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux) {
+ pr_err("%s: could not allocate mux clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &clk_mux_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ /* struct clk_mux assignments */
+ mux->reg = reg;
+ mux->shift = shift;
+ mux->mask = mask;
+ mux->flags = clk_mux_flags;
+ mux->lock = lock;
+ mux->table = table;
+ mux->hw.init = &init;
+
+ clk = clk_register(dev, &mux->hw);
+
+ if (IS_ERR(clk))
+ kfree(mux);
+
+ return clk;
+}
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock)
+{
+ u32 mask = BIT(width) - 1;
+
+ return clk_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, shift, mask, clk_mux_flags,
+ NULL, lock);
+}
diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c
new file mode 100644
index 000000000..6b4c70f7d
--- /dev/null
+++ b/drivers/clk/clk-nomadik.c
@@ -0,0 +1,48 @@
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+
+/*
+ * The Nomadik clock tree is described in the STN8815A12 DB V4.2
+ * reference manual for the chip, page 94 ff.
+ */
+
+void __init nomadik_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
+ clk_register_clkdev(clk, "apb_pclk", NULL);
+ clk_register_clkdev(clk, NULL, "gpio.0");
+ clk_register_clkdev(clk, NULL, "gpio.1");
+ clk_register_clkdev(clk, NULL, "gpio.2");
+ clk_register_clkdev(clk, NULL, "gpio.3");
+ clk_register_clkdev(clk, NULL, "rng");
+ clk_register_clkdev(clk, NULL, "fsmc-nand");
+
+ /*
+ * The 2.4 MHz TIMCLK reference clock is active at boot time, this is
+ * actually the MXTALCLK @19.2 MHz divided by 8. This clock is used
+ * by the timers and watchdog. See page 105 ff.
+ */
+ clk = clk_register_fixed_rate(NULL, "TIMCLK", NULL, CLK_IS_ROOT,
+ 2400000);
+ clk_register_clkdev(clk, NULL, "mtu0");
+ clk_register_clkdev(clk, NULL, "mtu1");
+
+ /*
+ * At boot time, PLL2 is set to generate a set of fixed clocks,
+ * one of them is CLK48, the 48 MHz clock, routed to the UART, MMC/SD
+ * I2C, IrDA, USB and SSP blocks.
+ */
+ clk = clk_register_fixed_rate(NULL, "CLK48", NULL, CLK_IS_ROOT,
+ 48000000);
+ clk_register_clkdev(clk, NULL, "uart0");
+ clk_register_clkdev(clk, NULL, "uart1");
+ clk_register_clkdev(clk, NULL, "mmci");
+ clk_register_clkdev(clk, NULL, "ssp");
+ clk_register_clkdev(clk, NULL, "nmk-i2c.0");
+ clk_register_clkdev(clk, NULL, "nmk-i2c.1");
+}
diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c
new file mode 100644
index 000000000..643ca653f
--- /dev/null
+++ b/drivers/clk/clk-prima2.c
@@ -0,0 +1,1126 @@
+/*
+ * Clock tree for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#define SIRFSOC_CLKC_CLK_EN0 0x0000
+#define SIRFSOC_CLKC_CLK_EN1 0x0004
+#define SIRFSOC_CLKC_REF_CFG 0x0014
+#define SIRFSOC_CLKC_CPU_CFG 0x0018
+#define SIRFSOC_CLKC_MEM_CFG 0x001c
+#define SIRFSOC_CLKC_SYS_CFG 0x0020
+#define SIRFSOC_CLKC_IO_CFG 0x0024
+#define SIRFSOC_CLKC_DSP_CFG 0x0028
+#define SIRFSOC_CLKC_GFX_CFG 0x002c
+#define SIRFSOC_CLKC_MM_CFG 0x0030
+#define SIRFSOC_CLKC_LCD_CFG 0x0034
+#define SIRFSOC_CLKC_MMC_CFG 0x0038
+#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
+#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
+#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
+#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
+#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
+#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
+#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
+#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
+#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
+#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
+#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
+#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
+#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
+
+static void *sirfsoc_clk_vbase, *sirfsoc_rsc_vbase;
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+/*
+ * SiRFprimaII clock controller
+ * - 2 oscillators: osc-26MHz, rtc-32.768KHz
+ * - 3 standard configurable plls: pll1, pll2 & pll3
+ * - 2 exclusive plls: usb phy pll and sata phy pll
+ * - 8 clock domains: cpu/cpudiv, mem/memdiv, sys/io, dsp, graphic, multimedia,
+ * display and sdphy.
+ * Each clock domain can select its own clock source from five clock sources,
+ * X_XIN, X_XINW, PLL1, PLL2 and PLL3. The domain clock is used as the source
+ * clock of the group clock.
+ * - dsp domain: gps, mf
+ * - io domain: dmac, nand, audio, uart, i2c, spi, usp, pwm, pulse
+ * - sys domain: security
+ */
+
+struct clk_pll {
+ struct clk_hw hw;
+ unsigned short regofs; /* register offset */
+};
+
+#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw)
+
+struct clk_dmn {
+ struct clk_hw hw;
+ signed char enable_bit; /* enable bit: 0 ~ 63 */
+ unsigned short regofs; /* register offset */
+};
+
+#define to_dmnclk(_hw) container_of(_hw, struct clk_dmn, hw)
+
+struct clk_std {
+ struct clk_hw hw;
+ signed char enable_bit; /* enable bit: 0 ~ 63 */
+};
+
+#define to_stdclk(_hw) container_of(_hw, struct clk_std, hw)
+
+static int std_clk_is_enabled(struct clk_hw *hw);
+static int std_clk_enable(struct clk_hw *hw);
+static void std_clk_disable(struct clk_hw *hw);
+
+static inline unsigned long clkc_readl(unsigned reg)
+{
+ return readl(sirfsoc_clk_vbase + reg);
+}
+
+static inline void clkc_writel(u32 val, unsigned reg)
+{
+ writel(val, sirfsoc_clk_vbase + reg);
+}
+
+/*
+ * std pll
+ */
+
+static unsigned long pll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long fin = parent_rate;
+ struct clk_pll *clk = to_pllclk(hw);
+ u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 -
+ SIRFSOC_CLKC_PLL1_CFG0;
+
+ if (clkc_readl(regcfg2) & BIT(2)) {
+ /* pll bypass mode */
+ return fin;
+ } else {
+ /* fout = fin * nf / nr / od */
+ u32 cfg0 = clkc_readl(clk->regofs);
+ u32 nf = (cfg0 & (BIT(13) - 1)) + 1;
+ u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1;
+ u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1;
+ WARN_ON(fin % MHZ);
+ return fin / MHZ * nf / nr / od * MHZ;
+ }
+}
+
+static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fin, nf, nr, od;
+
+ /*
+ * fout = fin * nf / (nr * od);
+ * set od = 1, nr = fin/MHz, so fout = nf * MHz
+ */
+ rate = rate - rate % MHZ;
+
+ nf = rate / MHZ;
+ if (nf > BIT(13))
+ nf = BIT(13);
+ if (nf < 1)
+ nf = 1;
+
+ fin = *parent_rate;
+
+ nr = fin / MHZ;
+ if (nr > BIT(6))
+ nr = BIT(6);
+ od = 1;
+
+ return fin * nf / (nr * od);
+}
+
+static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clk = to_pllclk(hw);
+ unsigned long fin, nf, nr, od, reg;
+
+ /*
+ * fout = fin * nf / (nr * od);
+ * set od = 1, nr = fin/MHz, so fout = nf * MHz
+ */
+
+ nf = rate / MHZ;
+ if (unlikely((rate % MHZ) || nf > BIT(13) || nf < 1))
+ return -EINVAL;
+
+ fin = parent_rate;
+ BUG_ON(fin < MHZ);
+
+ nr = fin / MHZ;
+ BUG_ON((fin % MHZ) || nr > BIT(6));
+
+ od = 1;
+
+ reg = (nf - 1) | ((nr - 1) << 13) | ((od - 1) << 19);
+ clkc_writel(reg, clk->regofs);
+
+ reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0;
+ clkc_writel((nf >> 1) - 1, reg);
+
+ reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0;
+ while (!(clkc_readl(reg) & BIT(6)))
+ cpu_relax();
+
+ return 0;
+}
+
+static struct clk_ops std_pll_ops = {
+ .recalc_rate = pll_clk_recalc_rate,
+ .round_rate = pll_clk_round_rate,
+ .set_rate = pll_clk_set_rate,
+};
+
+static const char *pll_clk_parents[] = {
+ "osc",
+};
+
+static struct clk_init_data clk_pll1_init = {
+ .name = "pll1",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_init_data clk_pll2_init = {
+ .name = "pll2",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_init_data clk_pll3_init = {
+ .name = "pll3",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_pll clk_pll1 = {
+ .regofs = SIRFSOC_CLKC_PLL1_CFG0,
+ .hw = {
+ .init = &clk_pll1_init,
+ },
+};
+
+static struct clk_pll clk_pll2 = {
+ .regofs = SIRFSOC_CLKC_PLL2_CFG0,
+ .hw = {
+ .init = &clk_pll2_init,
+ },
+};
+
+static struct clk_pll clk_pll3 = {
+ .regofs = SIRFSOC_CLKC_PLL3_CFG0,
+ .hw = {
+ .init = &clk_pll3_init,
+ },
+};
+
+/*
+ * usb uses specified pll
+ */
+
+static int usb_pll_clk_enable(struct clk_hw *hw)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ reg &= ~(SIRFSOC_USBPHY_PLL_POWERDOWN | SIRFSOC_USBPHY_PLL_BYPASS);
+ writel(reg, sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ while (!(readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL) &
+ SIRFSOC_USBPHY_PLL_LOCK))
+ cpu_relax();
+
+ return 0;
+}
+
+static void usb_pll_clk_disable(struct clk_hw *clk)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ reg |= (SIRFSOC_USBPHY_PLL_POWERDOWN | SIRFSOC_USBPHY_PLL_BYPASS);
+ writel(reg, sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+}
+
+static unsigned long usb_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ return (reg & SIRFSOC_USBPHY_PLL_BYPASS) ? parent_rate : 48*MHZ;
+}
+
+static struct clk_ops usb_pll_ops = {
+ .enable = usb_pll_clk_enable,
+ .disable = usb_pll_clk_disable,
+ .recalc_rate = usb_pll_clk_recalc_rate,
+};
+
+static struct clk_init_data clk_usb_pll_init = {
+ .name = "usb_pll",
+ .ops = &usb_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_hw usb_pll_clk_hw = {
+ .init = &clk_usb_pll_init,
+};
+
+/*
+ * clock domains - cpu, mem, sys/io, dsp, gfx
+ */
+
+static const char *dmn_clk_parents[] = {
+ "rtc",
+ "osc",
+ "pll1",
+ "pll2",
+ "pll3",
+};
+
+static u8 dmn_clk_get_parent(struct clk_hw *hw)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ u32 cfg = clkc_readl(clk->regofs);
+
+ /* parent of io domain can only be pll3 */
+ if (strcmp(hw->init->name, "io") == 0)
+ return 4;
+
+ WARN_ON((cfg & (BIT(3) - 1)) > 4);
+
+ return cfg & (BIT(3) - 1);
+}
+
+static int dmn_clk_set_parent(struct clk_hw *hw, u8 parent)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ u32 cfg = clkc_readl(clk->regofs);
+
+ /* parent of io domain can only be pll3 */
+ if (strcmp(hw->init->name, "io") == 0)
+ return -EINVAL;
+
+ cfg &= ~(BIT(3) - 1);
+ clkc_writel(cfg | parent, clk->regofs);
+ /* BIT(3) - switching status: 1 - busy, 0 - done */
+ while (clkc_readl(clk->regofs) & BIT(3))
+ cpu_relax();
+
+ return 0;
+}
+
+static unsigned long dmn_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+
+{
+ unsigned long fin = parent_rate;
+ struct clk_dmn *clk = to_dmnclk(hw);
+
+ u32 cfg = clkc_readl(clk->regofs);
+
+ if (cfg & BIT(24)) {
+ /* fcd bypass mode */
+ return fin;
+ } else {
+ /*
+ * wait count: bit[19:16], hold count: bit[23:20]
+ */
+ u32 wait = (cfg >> 16) & (BIT(4) - 1);
+ u32 hold = (cfg >> 20) & (BIT(4) - 1);
+
+ return fin / (wait + hold + 2);
+ }
+}
+
+static long dmn_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fin;
+ unsigned ratio, wait, hold;
+ unsigned bits = (strcmp(hw->init->name, "mem") == 0) ? 3 : 4;
+
+ fin = *parent_rate;
+ ratio = fin / rate;
+
+ if (ratio < 2)
+ ratio = 2;
+ if (ratio > BIT(bits + 1))
+ ratio = BIT(bits + 1);
+
+ wait = (ratio >> 1) - 1;
+ hold = ratio - wait - 2;
+
+ return fin / (wait + hold + 2);
+}
+
+static int dmn_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ unsigned long fin;
+ unsigned ratio, wait, hold, reg;
+ unsigned bits = (strcmp(hw->init->name, "mem") == 0) ? 3 : 4;
+
+ fin = parent_rate;
+ ratio = fin / rate;
+
+ if (unlikely(ratio < 2 || ratio > BIT(bits + 1)))
+ return -EINVAL;
+
+ WARN_ON(fin % rate);
+
+ wait = (ratio >> 1) - 1;
+ hold = ratio - wait - 2;
+
+ reg = clkc_readl(clk->regofs);
+ reg &= ~(((BIT(bits) - 1) << 16) | ((BIT(bits) - 1) << 20));
+ reg |= (wait << 16) | (hold << 20) | BIT(25);
+ clkc_writel(reg, clk->regofs);
+
+ /* waiting FCD been effective */
+ while (clkc_readl(clk->regofs) & BIT(25))
+ cpu_relax();
+
+ return 0;
+}
+
+static struct clk_ops msi_ops = {
+ .set_rate = dmn_clk_set_rate,
+ .round_rate = dmn_clk_round_rate,
+ .recalc_rate = dmn_clk_recalc_rate,
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+static struct clk_init_data clk_mem_init = {
+ .name = "mem",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mem = {
+ .regofs = SIRFSOC_CLKC_MEM_CFG,
+ .hw = {
+ .init = &clk_mem_init,
+ },
+};
+
+static struct clk_init_data clk_sys_init = {
+ .name = "sys",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+ .flags = CLK_SET_RATE_GATE,
+};
+
+static struct clk_dmn clk_sys = {
+ .regofs = SIRFSOC_CLKC_SYS_CFG,
+ .hw = {
+ .init = &clk_sys_init,
+ },
+};
+
+static struct clk_init_data clk_io_init = {
+ .name = "io",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_io = {
+ .regofs = SIRFSOC_CLKC_IO_CFG,
+ .hw = {
+ .init = &clk_io_init,
+ },
+};
+
+static struct clk_ops cpu_ops = {
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+static struct clk_init_data clk_cpu_init = {
+ .name = "cpu",
+ .ops = &cpu_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+};
+
+static struct clk_dmn clk_cpu = {
+ .regofs = SIRFSOC_CLKC_CPU_CFG,
+ .hw = {
+ .init = &clk_cpu_init,
+ },
+};
+
+static struct clk_ops dmn_ops = {
+ .is_enabled = std_clk_is_enabled,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .set_rate = dmn_clk_set_rate,
+ .round_rate = dmn_clk_round_rate,
+ .recalc_rate = dmn_clk_recalc_rate,
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+/* dsp, gfx, mm, lcd and vpp domain */
+
+static struct clk_init_data clk_dsp_init = {
+ .name = "dsp",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_dsp = {
+ .regofs = SIRFSOC_CLKC_DSP_CFG,
+ .enable_bit = 0,
+ .hw = {
+ .init = &clk_dsp_init,
+ },
+};
+
+static struct clk_init_data clk_gfx_init = {
+ .name = "gfx",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_gfx = {
+ .regofs = SIRFSOC_CLKC_GFX_CFG,
+ .enable_bit = 8,
+ .hw = {
+ .init = &clk_gfx_init,
+ },
+};
+
+static struct clk_init_data clk_mm_init = {
+ .name = "mm",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mm = {
+ .regofs = SIRFSOC_CLKC_MM_CFG,
+ .enable_bit = 9,
+ .hw = {
+ .init = &clk_mm_init,
+ },
+};
+
+static struct clk_init_data clk_lcd_init = {
+ .name = "lcd",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_lcd = {
+ .regofs = SIRFSOC_CLKC_LCD_CFG,
+ .enable_bit = 10,
+ .hw = {
+ .init = &clk_lcd_init,
+ },
+};
+
+static struct clk_init_data clk_vpp_init = {
+ .name = "vpp",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_vpp = {
+ .regofs = SIRFSOC_CLKC_LCD_CFG,
+ .enable_bit = 11,
+ .hw = {
+ .init = &clk_vpp_init,
+ },
+};
+
+static struct clk_init_data clk_mmc01_init = {
+ .name = "mmc01",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mmc01 = {
+ .regofs = SIRFSOC_CLKC_MMC_CFG,
+ .enable_bit = 59,
+ .hw = {
+ .init = &clk_mmc01_init,
+ },
+};
+
+static struct clk_init_data clk_mmc23_init = {
+ .name = "mmc23",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mmc23 = {
+ .regofs = SIRFSOC_CLKC_MMC_CFG,
+ .enable_bit = 60,
+ .hw = {
+ .init = &clk_mmc23_init,
+ },
+};
+
+static struct clk_init_data clk_mmc45_init = {
+ .name = "mmc45",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mmc45 = {
+ .regofs = SIRFSOC_CLKC_MMC_CFG,
+ .enable_bit = 61,
+ .hw = {
+ .init = &clk_mmc45_init,
+ },
+};
+
+/*
+ * peripheral controllers in io domain
+ */
+
+static int std_clk_is_enabled(struct clk_hw *hw)
+{
+ u32 reg;
+ int bit;
+ struct clk_std *clk = to_stdclk(hw);
+
+ bit = clk->enable_bit % 32;
+ reg = clk->enable_bit / 32;
+ reg = SIRFSOC_CLKC_CLK_EN0 + reg * sizeof(reg);
+
+ return !!(clkc_readl(reg) & BIT(bit));
+}
+
+static int std_clk_enable(struct clk_hw *hw)
+{
+ u32 val, reg;
+ int bit;
+ struct clk_std *clk = to_stdclk(hw);
+
+ BUG_ON(clk->enable_bit < 0 || clk->enable_bit > 63);
+
+ bit = clk->enable_bit % 32;
+ reg = clk->enable_bit / 32;
+ reg = SIRFSOC_CLKC_CLK_EN0 + reg * sizeof(reg);
+
+ val = clkc_readl(reg) | BIT(bit);
+ clkc_writel(val, reg);
+ return 0;
+}
+
+static void std_clk_disable(struct clk_hw *hw)
+{
+ u32 val, reg;
+ int bit;
+ struct clk_std *clk = to_stdclk(hw);
+
+ BUG_ON(clk->enable_bit < 0 || clk->enable_bit > 63);
+
+ bit = clk->enable_bit % 32;
+ reg = clk->enable_bit / 32;
+ reg = SIRFSOC_CLKC_CLK_EN0 + reg * sizeof(reg);
+
+ val = clkc_readl(reg) & ~BIT(bit);
+ clkc_writel(val, reg);
+}
+
+static const char *std_clk_io_parents[] = {
+ "io",
+};
+
+static struct clk_ops ios_ops = {
+ .is_enabled = std_clk_is_enabled,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+};
+
+static struct clk_init_data clk_dmac0_init = {
+ .name = "dmac0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_dmac0 = {
+ .enable_bit = 32,
+ .hw = {
+ .init = &clk_dmac0_init,
+ },
+};
+
+static struct clk_init_data clk_dmac1_init = {
+ .name = "dmac1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_dmac1 = {
+ .enable_bit = 33,
+ .hw = {
+ .init = &clk_dmac1_init,
+ },
+};
+
+static struct clk_init_data clk_nand_init = {
+ .name = "nand",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_nand = {
+ .enable_bit = 34,
+ .hw = {
+ .init = &clk_nand_init,
+ },
+};
+
+static struct clk_init_data clk_audio_init = {
+ .name = "audio",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_audio = {
+ .enable_bit = 35,
+ .hw = {
+ .init = &clk_audio_init,
+ },
+};
+
+static struct clk_init_data clk_uart0_init = {
+ .name = "uart0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_uart0 = {
+ .enable_bit = 36,
+ .hw = {
+ .init = &clk_uart0_init,
+ },
+};
+
+static struct clk_init_data clk_uart1_init = {
+ .name = "uart1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_uart1 = {
+ .enable_bit = 37,
+ .hw = {
+ .init = &clk_uart1_init,
+ },
+};
+
+static struct clk_init_data clk_uart2_init = {
+ .name = "uart2",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_uart2 = {
+ .enable_bit = 38,
+ .hw = {
+ .init = &clk_uart2_init,
+ },
+};
+
+static struct clk_init_data clk_usp0_init = {
+ .name = "usp0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_usp0 = {
+ .enable_bit = 39,
+ .hw = {
+ .init = &clk_usp0_init,
+ },
+};
+
+static struct clk_init_data clk_usp1_init = {
+ .name = "usp1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_usp1 = {
+ .enable_bit = 40,
+ .hw = {
+ .init = &clk_usp1_init,
+ },
+};
+
+static struct clk_init_data clk_usp2_init = {
+ .name = "usp2",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_usp2 = {
+ .enable_bit = 41,
+ .hw = {
+ .init = &clk_usp2_init,
+ },
+};
+
+static struct clk_init_data clk_vip_init = {
+ .name = "vip",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_vip = {
+ .enable_bit = 42,
+ .hw = {
+ .init = &clk_vip_init,
+ },
+};
+
+static struct clk_init_data clk_spi0_init = {
+ .name = "spi0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_spi0 = {
+ .enable_bit = 43,
+ .hw = {
+ .init = &clk_spi0_init,
+ },
+};
+
+static struct clk_init_data clk_spi1_init = {
+ .name = "spi1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_spi1 = {
+ .enable_bit = 44,
+ .hw = {
+ .init = &clk_spi1_init,
+ },
+};
+
+static struct clk_init_data clk_tsc_init = {
+ .name = "tsc",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_tsc = {
+ .enable_bit = 45,
+ .hw = {
+ .init = &clk_tsc_init,
+ },
+};
+
+static struct clk_init_data clk_i2c0_init = {
+ .name = "i2c0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_i2c0 = {
+ .enable_bit = 46,
+ .hw = {
+ .init = &clk_i2c0_init,
+ },
+};
+
+static struct clk_init_data clk_i2c1_init = {
+ .name = "i2c1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_i2c1 = {
+ .enable_bit = 47,
+ .hw = {
+ .init = &clk_i2c1_init,
+ },
+};
+
+static struct clk_init_data clk_pwmc_init = {
+ .name = "pwmc",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_pwmc = {
+ .enable_bit = 48,
+ .hw = {
+ .init = &clk_pwmc_init,
+ },
+};
+
+static struct clk_init_data clk_efuse_init = {
+ .name = "efuse",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_efuse = {
+ .enable_bit = 49,
+ .hw = {
+ .init = &clk_efuse_init,
+ },
+};
+
+static struct clk_init_data clk_pulse_init = {
+ .name = "pulse",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_pulse = {
+ .enable_bit = 50,
+ .hw = {
+ .init = &clk_pulse_init,
+ },
+};
+
+static const char *std_clk_dsp_parents[] = {
+ "dsp",
+};
+
+static struct clk_init_data clk_gps_init = {
+ .name = "gps",
+ .ops = &ios_ops,
+ .parent_names = std_clk_dsp_parents,
+ .num_parents = ARRAY_SIZE(std_clk_dsp_parents),
+};
+
+static struct clk_std clk_gps = {
+ .enable_bit = 1,
+ .hw = {
+ .init = &clk_gps_init,
+ },
+};
+
+static struct clk_init_data clk_mf_init = {
+ .name = "mf",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_mf = {
+ .enable_bit = 2,
+ .hw = {
+ .init = &clk_mf_init,
+ },
+};
+
+static const char *std_clk_sys_parents[] = {
+ "sys",
+};
+
+static struct clk_init_data clk_security_init = {
+ .name = "mf",
+ .ops = &ios_ops,
+ .parent_names = std_clk_sys_parents,
+ .num_parents = ARRAY_SIZE(std_clk_sys_parents),
+};
+
+static struct clk_std clk_security = {
+ .enable_bit = 19,
+ .hw = {
+ .init = &clk_security_init,
+ },
+};
+
+static const char *std_clk_usb_parents[] = {
+ "usb_pll",
+};
+
+static struct clk_init_data clk_usb0_init = {
+ .name = "usb0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_usb_parents,
+ .num_parents = ARRAY_SIZE(std_clk_usb_parents),
+};
+
+static struct clk_std clk_usb0 = {
+ .enable_bit = 16,
+ .hw = {
+ .init = &clk_usb0_init,
+ },
+};
+
+static struct clk_init_data clk_usb1_init = {
+ .name = "usb1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_usb_parents,
+ .num_parents = ARRAY_SIZE(std_clk_usb_parents),
+};
+
+static struct clk_std clk_usb1 = {
+ .enable_bit = 17,
+ .hw = {
+ .init = &clk_usb1_init,
+ },
+};
+
+static struct of_device_id clkc_ids[] = {
+ { .compatible = "sirf,prima2-clkc" },
+ {},
+};
+
+static struct of_device_id rsc_ids[] = {
+ { .compatible = "sirf,prima2-rsc" },
+ {},
+};
+
+enum prima2_clk_index {
+ /* 0 1 2 3 4 5 6 7 8 9 */
+ rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps,
+ mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0,
+ spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1,
+ usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll,
+ usb0, usb1, maxclk,
+};
+
+static __initdata struct clk_hw* prima2_clk_hw_array[maxclk] = {
+ NULL, /* dummy */
+ NULL,
+ &clk_pll1.hw,
+ &clk_pll2.hw,
+ &clk_pll3.hw,
+ &clk_mem.hw,
+ &clk_sys.hw,
+ &clk_security.hw,
+ &clk_dsp.hw,
+ &clk_gps.hw,
+ &clk_mf.hw,
+ &clk_io.hw,
+ &clk_cpu.hw,
+ &clk_uart0.hw,
+ &clk_uart1.hw,
+ &clk_uart2.hw,
+ &clk_tsc.hw,
+ &clk_i2c0.hw,
+ &clk_i2c1.hw,
+ &clk_spi0.hw,
+ &clk_spi1.hw,
+ &clk_pwmc.hw,
+ &clk_efuse.hw,
+ &clk_pulse.hw,
+ &clk_dmac0.hw,
+ &clk_dmac1.hw,
+ &clk_nand.hw,
+ &clk_audio.hw,
+ &clk_usp0.hw,
+ &clk_usp1.hw,
+ &clk_usp2.hw,
+ &clk_vip.hw,
+ &clk_gfx.hw,
+ &clk_mm.hw,
+ &clk_lcd.hw,
+ &clk_vpp.hw,
+ &clk_mmc01.hw,
+ &clk_mmc23.hw,
+ &clk_mmc45.hw,
+ &usb_pll_clk_hw,
+ &clk_usb0.hw,
+ &clk_usb1.hw,
+};
+
+static struct clk *prima2_clks[maxclk];
+static struct clk_onecell_data clk_data;
+
+void __init sirfsoc_of_clk_init(void)
+{
+ struct device_node *np;
+ int i;
+
+ np = of_find_matching_node(NULL, rsc_ids);
+ if (!np)
+ panic("unable to find compatible rsc node in dtb\n");
+
+ sirfsoc_rsc_vbase = of_iomap(np, 0);
+ if (!sirfsoc_rsc_vbase)
+ panic("unable to map rsc registers\n");
+
+ of_node_put(np);
+
+ np = of_find_matching_node(NULL, clkc_ids);
+ if (!np)
+ return;
+
+ sirfsoc_clk_vbase = of_iomap(np, 0);
+ if (!sirfsoc_clk_vbase)
+ panic("unable to map clkc registers\n");
+
+ /* These are always available (RTC and 26MHz OSC)*/
+ prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL,
+ CLK_IS_ROOT, 32768);
+ prima2_clks[osc]= clk_register_fixed_rate(NULL, "osc", NULL,
+ CLK_IS_ROOT, 26000000);
+
+ for (i = pll1; i < maxclk; i++) {
+ prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
+ BUG_ON(IS_ERR(prima2_clks[i]));
+ }
+ clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
+ clk_register_clkdev(prima2_clks[io], NULL, "io");
+ clk_register_clkdev(prima2_clks[mem], NULL, "mem");
+
+ clk_data.clks = prima2_clks;
+ clk_data.clk_num = maxclk;
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
new file mode 100644
index 000000000..24f553673
--- /dev/null
+++ b/drivers/clk/clk-si5351.c
@@ -0,0 +1,1520 @@
+/*
+ * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * References:
+ * [1] "Si5351A/B/C Data Sheet"
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+ * [2] "Manually Generating an Si5351 Register Map"
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/rational.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/si5351.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/div64.h>
+
+#include "clk-si5351.h"
+
+struct si5351_driver_data;
+
+struct si5351_parameters {
+ unsigned long p1;
+ unsigned long p2;
+ unsigned long p3;
+ int valid;
+};
+
+struct si5351_hw_data {
+ struct clk_hw hw;
+ struct si5351_driver_data *drvdata;
+ struct si5351_parameters params;
+ unsigned char num;
+};
+
+struct si5351_driver_data {
+ enum si5351_variant variant;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct clk_onecell_data onecell;
+
+ struct clk *pxtal;
+ const char *pxtal_name;
+ struct clk_hw xtal;
+ struct clk *pclkin;
+ const char *pclkin_name;
+ struct clk_hw clkin;
+
+ struct si5351_hw_data pll[2];
+ struct si5351_hw_data *msynth;
+ struct si5351_hw_data *clkout;
+};
+
+static const char const *si5351_input_names[] = {
+ "xtal", "clkin"
+};
+static const char const *si5351_pll_names[] = {
+ "plla", "pllb", "vxco"
+};
+static const char const *si5351_msynth_names[] = {
+ "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
+};
+static const char const *si5351_clkout_names[] = {
+ "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
+};
+
+/*
+ * Si5351 i2c regmap
+ */
+static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(drvdata->regmap, reg, &val);
+ if (ret) {
+ dev_err(&drvdata->client->dev,
+ "unable to read from reg%02x\n", reg);
+ return 0;
+ }
+
+ return (u8)val;
+}
+
+static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
+ u8 reg, u8 count, u8 *buf)
+{
+ return regmap_bulk_read(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
+ u8 reg, u8 val)
+{
+ return regmap_write(drvdata->regmap, reg, val);
+}
+
+static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
+ u8 reg, u8 count, const u8 *buf)
+{
+ return regmap_raw_write(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
+ u8 reg, u8 mask, u8 val)
+{
+ return regmap_update_bits(drvdata->regmap, reg, mask, val);
+}
+
+static inline u8 si5351_msynth_params_address(int num)
+{
+ if (num > 5)
+ return SI5351_CLK6_PARAMETERS + (num - 6);
+ return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
+}
+
+static void si5351_read_parameters(struct si5351_driver_data *drvdata,
+ u8 reg, struct si5351_parameters *params)
+{
+ u8 buf[SI5351_PARAMETERS_LENGTH];
+
+ switch (reg) {
+ case SI5351_CLK6_PARAMETERS:
+ case SI5351_CLK7_PARAMETERS:
+ buf[0] = si5351_reg_read(drvdata, reg);
+ params->p1 = buf[0];
+ params->p2 = 0;
+ params->p3 = 1;
+ break;
+ default:
+ si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+ params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
+ params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
+ params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
+ }
+ params->valid = 1;
+}
+
+static void si5351_write_parameters(struct si5351_driver_data *drvdata,
+ u8 reg, struct si5351_parameters *params)
+{
+ u8 buf[SI5351_PARAMETERS_LENGTH];
+
+ switch (reg) {
+ case SI5351_CLK6_PARAMETERS:
+ case SI5351_CLK7_PARAMETERS:
+ buf[0] = params->p1 & 0xff;
+ si5351_reg_write(drvdata, reg, buf[0]);
+ break;
+ default:
+ buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
+ buf[1] = params->p3 & 0xff;
+ /* save rdiv and divby4 */
+ buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
+ buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
+ buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
+ buf[4] = params->p1 & 0xff;
+ buf[5] = ((params->p3 & 0xf0000) >> 12) |
+ ((params->p2 & 0xf0000) >> 16);
+ buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
+ buf[7] = params->p2 & 0xff;
+ si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+ }
+}
+
+static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SI5351_DEVICE_STATUS:
+ case SI5351_INTERRUPT_STATUS:
+ case SI5351_PLL_RESET:
+ return true;
+ }
+ return false;
+}
+
+static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
+{
+ /* reserved registers */
+ if (reg >= 4 && reg <= 8)
+ return false;
+ if (reg >= 10 && reg <= 14)
+ return false;
+ if (reg >= 173 && reg <= 176)
+ return false;
+ if (reg >= 178 && reg <= 182)
+ return false;
+ /* read-only */
+ if (reg == SI5351_DEVICE_STATUS)
+ return false;
+ return true;
+}
+
+static struct regmap_config si5351_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 187,
+ .writeable_reg = si5351_regmap_is_writeable,
+ .volatile_reg = si5351_regmap_is_volatile,
+};
+
+/*
+ * Si5351 xtal clock input
+ */
+static int si5351_xtal_prepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, xtal);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
+ return 0;
+}
+
+static void si5351_xtal_unprepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, xtal);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_XTAL_ENABLE, 0);
+}
+
+static const struct clk_ops si5351_xtal_ops = {
+ .prepare = si5351_xtal_prepare,
+ .unprepare = si5351_xtal_unprepare,
+};
+
+/*
+ * Si5351 clkin clock input (Si5351C only)
+ */
+static int si5351_clkin_prepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, clkin);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
+ return 0;
+}
+
+static void si5351_clkin_unprepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, clkin);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_CLKIN_ENABLE, 0);
+}
+
+/*
+ * CMOS clock source constraints:
+ * The input frequency range of the PLL is 10Mhz to 40MHz.
+ * If CLKIN is >40MHz, the input divider must be used.
+ */
+static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, clkin);
+ unsigned long rate;
+ unsigned char idiv;
+
+ rate = parent_rate;
+ if (parent_rate > 160000000) {
+ idiv = SI5351_CLKIN_DIV_8;
+ rate /= 8;
+ } else if (parent_rate > 80000000) {
+ idiv = SI5351_CLKIN_DIV_4;
+ rate /= 4;
+ } else if (parent_rate > 40000000) {
+ idiv = SI5351_CLKIN_DIV_2;
+ rate /= 2;
+ } else {
+ idiv = SI5351_CLKIN_DIV_1;
+ }
+
+ si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+ SI5351_CLKIN_DIV_MASK, idiv);
+
+ dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
+ __func__, (1 << (idiv >> 6)), rate);
+
+ return rate;
+}
+
+static const struct clk_ops si5351_clkin_ops = {
+ .prepare = si5351_clkin_prepare,
+ .unprepare = si5351_clkin_unprepare,
+ .recalc_rate = si5351_clkin_recalc_rate,
+};
+
+/*
+ * Si5351 vxco clock input (Si5351B only)
+ */
+
+static int si5351_vxco_prepare(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n");
+
+ return 0;
+}
+
+static void si5351_vxco_unprepare(struct clk_hw *hw)
+{
+}
+
+static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 0;
+}
+
+static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent)
+{
+ return 0;
+}
+
+static const struct clk_ops si5351_vxco_ops = {
+ .prepare = si5351_vxco_prepare,
+ .unprepare = si5351_vxco_unprepare,
+ .recalc_rate = si5351_vxco_recalc_rate,
+ .set_rate = si5351_vxco_set_rate,
+};
+
+/*
+ * Si5351 pll a/b
+ *
+ * Feedback Multisynth Divider Equations [2]
+ *
+ * fVCO = fIN * (a + b/c)
+ *
+ * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and
+ * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV
+ *
+ * Feedback Multisynth Register Equations
+ *
+ * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * (3) MSNx_P3[19:0] = c
+ *
+ * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c
+ *
+ * Using (4) on (1) yields:
+ * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512
+ * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c
+ *
+ * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128
+ * = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3)
+ *
+ */
+static int _si5351_pll_reparent(struct si5351_driver_data *drvdata,
+ int num, enum si5351_pll_src parent)
+{
+ u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+
+ if (parent == SI5351_PLL_SRC_DEFAULT)
+ return 0;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (drvdata->variant != SI5351_VARIANT_C &&
+ parent != SI5351_PLL_SRC_XTAL)
+ return -EINVAL;
+
+ si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask,
+ (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask);
+ return 0;
+}
+
+static unsigned char si5351_pll_get_parent(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+ u8 val;
+
+ val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE);
+
+ return (val & mask) ? 1 : 0;
+}
+
+static int si5351_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ if (hwdata->drvdata->variant != SI5351_VARIANT_C &&
+ index > 0)
+ return -EPERM;
+
+ if (index > 1)
+ return -EINVAL;
+
+ return _si5351_pll_reparent(hwdata->drvdata, hwdata->num,
+ (index == 0) ? SI5351_PLL_SRC_XTAL :
+ SI5351_PLL_SRC_CLKIN);
+}
+
+static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+ SI5351_PLLB_PARAMETERS;
+ unsigned long long rate;
+
+ if (!hwdata->params.valid)
+ si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ if (hwdata->params.p3 == 0)
+ return parent_rate;
+
+ /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */
+ rate = hwdata->params.p1 * hwdata->params.p3;
+ rate += 512 * hwdata->params.p3;
+ rate += hwdata->params.p2;
+ rate *= parent_rate;
+ do_div(rate, 128 * hwdata->params.p3);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ parent_rate, (unsigned long)rate);
+
+ return (unsigned long)rate;
+}
+
+static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned long rfrac, denom, a, b, c;
+ unsigned long long lltmp;
+
+ if (rate < SI5351_PLL_VCO_MIN)
+ rate = SI5351_PLL_VCO_MIN;
+ if (rate > SI5351_PLL_VCO_MAX)
+ rate = SI5351_PLL_VCO_MAX;
+
+ /* determine integer part of feedback equation */
+ a = rate / *parent_rate;
+
+ if (a < SI5351_PLL_A_MIN)
+ rate = *parent_rate * SI5351_PLL_A_MIN;
+ if (a > SI5351_PLL_A_MAX)
+ rate = *parent_rate * SI5351_PLL_A_MAX;
+
+ /* find best approximation for b/c = fVCO mod fIN */
+ denom = 1000 * 1000;
+ lltmp = rate % (*parent_rate);
+ lltmp *= denom;
+ do_div(lltmp, *parent_rate);
+ rfrac = (unsigned long)lltmp;
+
+ b = 0;
+ c = 1;
+ if (rfrac)
+ rational_best_approximation(rfrac, denom,
+ SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
+
+ /* calculate parameters */
+ hwdata->params.p3 = c;
+ hwdata->params.p2 = (128 * b) % c;
+ hwdata->params.p1 = 128 * a;
+ hwdata->params.p1 += (128 * b / c);
+ hwdata->params.p1 -= 512;
+
+ /* recalculate rate by fIN * (a + b/c) */
+ lltmp = *parent_rate;
+ lltmp *= b;
+ do_div(lltmp, c);
+
+ rate = (unsigned long)lltmp;
+ rate += *parent_rate * a;
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), a, b, c,
+ *parent_rate, rate);
+
+ return rate;
+}
+
+static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+ SI5351_PLLB_PARAMETERS;
+
+ /* write multisynth parameters */
+ si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ /* plla/pllb ctrl is in clk6/clk7 ctrl registers */
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num,
+ SI5351_CLK_INTEGER_MODE,
+ (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ parent_rate, rate);
+
+ return 0;
+}
+
+static const struct clk_ops si5351_pll_ops = {
+ .set_parent = si5351_pll_set_parent,
+ .get_parent = si5351_pll_get_parent,
+ .recalc_rate = si5351_pll_recalc_rate,
+ .round_rate = si5351_pll_round_rate,
+ .set_rate = si5351_pll_set_rate,
+};
+
+/*
+ * Si5351 multisync divider
+ *
+ * for fOUT <= 150 MHz:
+ *
+ * fOUT = (fIN * (a + b/c)) / CLKOUTDIV
+ *
+ * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and
+ * fIN = fVCO0, fVCO1
+ *
+ * Output Clock Multisynth Register Equations
+ *
+ * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * MSx_P3[19:0] = c
+ *
+ * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
+ *
+ * for 150MHz < fOUT <= 160MHz:
+ *
+ * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b
+ */
+static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata,
+ int num, enum si5351_multisynth_src parent)
+{
+ if (parent == SI5351_MULTISYNTH_SRC_DEFAULT)
+ return 0;
+
+ if (num > 8)
+ return -EINVAL;
+
+ si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT,
+ (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 :
+ SI5351_CLK_PLL_SELECT);
+ return 0;
+}
+
+static unsigned char si5351_msynth_get_parent(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 val;
+
+ val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+
+ return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0;
+}
+
+static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num,
+ (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 :
+ SI5351_MULTISYNTH_SRC_VCO1);
+}
+
+static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = si5351_msynth_params_address(hwdata->num);
+ unsigned long long rate;
+ unsigned long m;
+
+ if (!hwdata->params.valid)
+ si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ if (hwdata->params.p3 == 0)
+ return parent_rate;
+
+ /*
+ * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
+ * multisync6-7: fOUT = fIN / P1
+ */
+ rate = parent_rate;
+ if (hwdata->num > 5) {
+ m = hwdata->params.p1;
+ } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
+ SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
+ m = 4;
+ } else {
+ rate *= 128 * hwdata->params.p3;
+ m = hwdata->params.p1 * hwdata->params.p3;
+ m += hwdata->params.p2;
+ m += 512 * hwdata->params.p3;
+ }
+
+ if (m == 0)
+ return 0;
+ do_div(rate, m);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ m, parent_rate, (unsigned long)rate);
+
+ return (unsigned long)rate;
+}
+
+static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned long long lltmp;
+ unsigned long a, b, c;
+ int divby4;
+
+ /* multisync6-7 can only handle freqencies < 150MHz */
+ if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
+ rate = SI5351_MULTISYNTH67_MAX_FREQ;
+
+ /* multisync frequency is 1MHz .. 160MHz */
+ if (rate > SI5351_MULTISYNTH_MAX_FREQ)
+ rate = SI5351_MULTISYNTH_MAX_FREQ;
+ if (rate < SI5351_MULTISYNTH_MIN_FREQ)
+ rate = SI5351_MULTISYNTH_MIN_FREQ;
+
+ divby4 = 0;
+ if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+ divby4 = 1;
+
+ /* multisync can set pll */
+ if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+ /*
+ * find largest integer divider for max
+ * vco frequency and given target rate
+ */
+ if (divby4 == 0) {
+ lltmp = SI5351_PLL_VCO_MAX;
+ do_div(lltmp, rate);
+ a = (unsigned long)lltmp;
+ } else
+ a = 4;
+
+ b = 0;
+ c = 1;
+
+ *parent_rate = a * rate;
+ } else {
+ unsigned long rfrac, denom;
+
+ /* disable divby4 */
+ if (divby4) {
+ rate = SI5351_MULTISYNTH_DIVBY4_FREQ;
+ divby4 = 0;
+ }
+
+ /* determine integer part of divider equation */
+ a = *parent_rate / rate;
+ if (a < SI5351_MULTISYNTH_A_MIN)
+ a = SI5351_MULTISYNTH_A_MIN;
+ if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
+ a = SI5351_MULTISYNTH67_A_MAX;
+ else if (a > SI5351_MULTISYNTH_A_MAX)
+ a = SI5351_MULTISYNTH_A_MAX;
+
+ /* find best approximation for b/c = fVCO mod fOUT */
+ denom = 1000 * 1000;
+ lltmp = (*parent_rate) % rate;
+ lltmp *= denom;
+ do_div(lltmp, rate);
+ rfrac = (unsigned long)lltmp;
+
+ b = 0;
+ c = 1;
+ if (rfrac)
+ rational_best_approximation(rfrac, denom,
+ SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX,
+ &b, &c);
+ }
+
+ /* recalculate rate by fOUT = fIN / (a + b/c) */
+ lltmp = *parent_rate;
+ lltmp *= c;
+ do_div(lltmp, a * c + b);
+ rate = (unsigned long)lltmp;
+
+ /* calculate parameters */
+ if (divby4) {
+ hwdata->params.p3 = 1;
+ hwdata->params.p2 = 0;
+ hwdata->params.p1 = 0;
+ } else {
+ hwdata->params.p3 = c;
+ hwdata->params.p2 = (128 * b) % c;
+ hwdata->params.p1 = 128 * a;
+ hwdata->params.p1 += (128 * b / c);
+ hwdata->params.p1 -= 512;
+ }
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4,
+ *parent_rate, rate);
+
+ return rate;
+}
+
+static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = si5351_msynth_params_address(hwdata->num);
+ int divby4 = 0;
+
+ /* write multisynth parameters */
+ si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+ divby4 = 1;
+
+ /* enable/disable integer mode and divby4 on multisynth0-5 */
+ if (hwdata->num < 6) {
+ si5351_set_bits(hwdata->drvdata, reg + 2,
+ SI5351_OUTPUT_CLK_DIVBY4,
+ (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0);
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_INTEGER_MODE,
+ (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+ }
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ divby4, parent_rate, rate);
+
+ return 0;
+}
+
+static const struct clk_ops si5351_msynth_ops = {
+ .set_parent = si5351_msynth_set_parent,
+ .get_parent = si5351_msynth_get_parent,
+ .recalc_rate = si5351_msynth_recalc_rate,
+ .round_rate = si5351_msynth_round_rate,
+ .set_rate = si5351_msynth_set_rate,
+};
+
+/*
+ * Si5351 clkout divider
+ */
+static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
+ int num, enum si5351_clkout_src parent)
+{
+ u8 val;
+
+ if (num > 8)
+ return -EINVAL;
+
+ switch (parent) {
+ case SI5351_CLKOUT_SRC_MSYNTH_N:
+ val = SI5351_CLK_INPUT_MULTISYNTH_N;
+ break;
+ case SI5351_CLKOUT_SRC_MSYNTH_0_4:
+ /* clk0/clk4 can only connect to its own multisync */
+ if (num == 0 || num == 4)
+ val = SI5351_CLK_INPUT_MULTISYNTH_N;
+ else
+ val = SI5351_CLK_INPUT_MULTISYNTH_0_4;
+ break;
+ case SI5351_CLKOUT_SRC_XTAL:
+ val = SI5351_CLK_INPUT_XTAL;
+ break;
+ case SI5351_CLKOUT_SRC_CLKIN:
+ if (drvdata->variant != SI5351_VARIANT_C)
+ return -EINVAL;
+
+ val = SI5351_CLK_INPUT_CLKIN;
+ break;
+ default:
+ return 0;
+ }
+
+ si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+ SI5351_CLK_INPUT_MASK, val);
+ return 0;
+}
+
+static int _si5351_clkout_set_drive_strength(
+ struct si5351_driver_data *drvdata, int num,
+ enum si5351_drive_strength drive)
+{
+ u8 mask;
+
+ if (num > 8)
+ return -EINVAL;
+
+ switch (drive) {
+ case SI5351_DRIVE_2MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_2MA;
+ break;
+ case SI5351_DRIVE_4MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_4MA;
+ break;
+ case SI5351_DRIVE_6MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_6MA;
+ break;
+ case SI5351_DRIVE_8MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_8MA;
+ break;
+ default:
+ return 0;
+ }
+
+ si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+ SI5351_CLK_DRIVE_STRENGTH_MASK, mask);
+ return 0;
+}
+
+static int si5351_clkout_prepare(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_POWERDOWN, 0);
+ si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+ (1 << hwdata->num), 0);
+ return 0;
+}
+
+static void si5351_clkout_unprepare(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN);
+ si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+ (1 << hwdata->num), (1 << hwdata->num));
+}
+
+static u8 si5351_clkout_get_parent(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ int index = 0;
+ unsigned char val;
+
+ val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+ switch (val & SI5351_CLK_INPUT_MASK) {
+ case SI5351_CLK_INPUT_MULTISYNTH_N:
+ index = 0;
+ break;
+ case SI5351_CLK_INPUT_MULTISYNTH_0_4:
+ index = 1;
+ break;
+ case SI5351_CLK_INPUT_XTAL:
+ index = 2;
+ break;
+ case SI5351_CLK_INPUT_CLKIN:
+ index = 3;
+ break;
+ }
+
+ return index;
+}
+
+static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT;
+
+ switch (index) {
+ case 0:
+ parent = SI5351_CLKOUT_SRC_MSYNTH_N;
+ break;
+ case 1:
+ parent = SI5351_CLKOUT_SRC_MSYNTH_0_4;
+ break;
+ case 2:
+ parent = SI5351_CLKOUT_SRC_XTAL;
+ break;
+ case 3:
+ parent = SI5351_CLKOUT_SRC_CLKIN;
+ break;
+ }
+
+ return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent);
+}
+
+static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned char reg;
+ unsigned char rdiv;
+
+ if (hwdata->num <= 5)
+ reg = si5351_msynth_params_address(hwdata->num) + 2;
+ else
+ reg = SI5351_CLK6_7_OUTPUT_DIVIDER;
+
+ rdiv = si5351_reg_read(hwdata->drvdata, reg);
+ if (hwdata->num == 6) {
+ rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK;
+ } else {
+ rdiv &= SI5351_OUTPUT_CLK_DIV_MASK;
+ rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT;
+ }
+
+ return parent_rate >> rdiv;
+}
+
+static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned char rdiv;
+
+ /* clkout6/7 can only handle output freqencies < 150MHz */
+ if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
+ rate = SI5351_CLKOUT67_MAX_FREQ;
+
+ /* clkout freqency is 8kHz - 160MHz */
+ if (rate > SI5351_CLKOUT_MAX_FREQ)
+ rate = SI5351_CLKOUT_MAX_FREQ;
+ if (rate < SI5351_CLKOUT_MIN_FREQ)
+ rate = SI5351_CLKOUT_MIN_FREQ;
+
+ /* request frequency if multisync master */
+ if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+ /* use r divider for frequencies below 1MHz */
+ rdiv = SI5351_OUTPUT_CLK_DIV_1;
+ while (rate < SI5351_MULTISYNTH_MIN_FREQ &&
+ rdiv < SI5351_OUTPUT_CLK_DIV_128) {
+ rdiv += 1;
+ rate *= 2;
+ }
+ *parent_rate = rate;
+ } else {
+ unsigned long new_rate, new_err, err;
+
+ /* round to closed rdiv */
+ rdiv = SI5351_OUTPUT_CLK_DIV_1;
+ new_rate = *parent_rate;
+ err = abs(new_rate - rate);
+ do {
+ new_rate >>= 1;
+ new_err = abs(new_rate - rate);
+ if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+ break;
+ rdiv++;
+ err = new_err;
+ } while (1);
+ }
+ rate = *parent_rate >> rdiv;
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+ *parent_rate, rate);
+
+ return rate;
+}
+
+static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned long new_rate, new_err, err;
+ unsigned char rdiv;
+
+ /* round to closed rdiv */
+ rdiv = SI5351_OUTPUT_CLK_DIV_1;
+ new_rate = parent_rate;
+ err = abs(new_rate - rate);
+ do {
+ new_rate >>= 1;
+ new_err = abs(new_rate - rate);
+ if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+ break;
+ rdiv++;
+ err = new_err;
+ } while (1);
+
+ /* write output divider */
+ switch (hwdata->num) {
+ case 6:
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+ SI5351_OUTPUT_CLK6_DIV_MASK, rdiv);
+ break;
+ case 7:
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+ SI5351_OUTPUT_CLK_DIV_MASK,
+ rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+ break;
+ default:
+ si5351_set_bits(hwdata->drvdata,
+ si5351_msynth_params_address(hwdata->num) + 2,
+ SI5351_OUTPUT_CLK_DIV_MASK,
+ rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+ }
+
+ /* powerup clkout */
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_POWERDOWN, 0);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+ parent_rate, rate);
+
+ return 0;
+}
+
+static const struct clk_ops si5351_clkout_ops = {
+ .prepare = si5351_clkout_prepare,
+ .unprepare = si5351_clkout_unprepare,
+ .set_parent = si5351_clkout_set_parent,
+ .get_parent = si5351_clkout_get_parent,
+ .recalc_rate = si5351_clkout_recalc_rate,
+ .round_rate = si5351_clkout_round_rate,
+ .set_rate = si5351_clkout_set_rate,
+};
+
+/*
+ * Si5351 i2c probe and DT
+ */
+#ifdef CONFIG_OF
+static const struct of_device_id si5351_dt_ids[] = {
+ { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, },
+ { .compatible = "silabs,si5351a-msop",
+ .data = (void *)SI5351_VARIANT_A3, },
+ { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, },
+ { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, si5351_dt_ids);
+
+static int si5351_dt_parse(struct i2c_client *client)
+{
+ struct device_node *child, *np = client->dev.of_node;
+ struct si5351_platform_data *pdata;
+ const struct of_device_id *match;
+ struct property *prop;
+ const __be32 *p;
+ int num = 0;
+ u32 val;
+
+ if (np == NULL)
+ return 0;
+
+ match = of_match_node(si5351_dt_ids, np);
+ if (match == NULL)
+ return -EINVAL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->variant = (enum si5351_variant)match->data;
+ pdata->clk_xtal = of_clk_get(np, 0);
+ if (!IS_ERR(pdata->clk_xtal))
+ clk_put(pdata->clk_xtal);
+ pdata->clk_clkin = of_clk_get(np, 1);
+ if (!IS_ERR(pdata->clk_clkin))
+ clk_put(pdata->clk_clkin);
+
+ /*
+ * property silabs,pll-source : <num src>, [<..>]
+ * allow to selectively set pll source
+ */
+ of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
+ if (num >= 2) {
+ dev_err(&client->dev,
+ "invalid pll %d on pll-source prop\n", num);
+ return -EINVAL;
+ }
+
+ p = of_prop_next_u32(prop, p, &val);
+ if (!p) {
+ dev_err(&client->dev,
+ "missing pll-source for pll %d\n", num);
+ return -EINVAL;
+ }
+
+ switch (val) {
+ case 0:
+ pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
+ break;
+ case 1:
+ if (pdata->variant != SI5351_VARIANT_C) {
+ dev_err(&client->dev,
+ "invalid parent %d for pll %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid parent %d for pll %d\n", val, num);
+ return -EINVAL;
+ }
+ }
+
+ /* per clkout properties */
+ for_each_child_of_node(np, child) {
+ if (of_property_read_u32(child, "reg", &num)) {
+ dev_err(&client->dev, "missing reg property of %s\n",
+ child->name);
+ return -EINVAL;
+ }
+
+ if (num >= 8 ||
+ (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) {
+ dev_err(&client->dev, "invalid clkout %d\n", num);
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(child, "silabs,multisynth-source",
+ &val)) {
+ switch (val) {
+ case 0:
+ pdata->clkout[num].multisynth_src =
+ SI5351_MULTISYNTH_SRC_VCO0;
+ break;
+ case 1:
+ pdata->clkout[num].multisynth_src =
+ SI5351_MULTISYNTH_SRC_VCO1;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid parent %d for multisynth %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "silabs,clock-source", &val)) {
+ switch (val) {
+ case 0:
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_MSYNTH_N;
+ break;
+ case 1:
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_MSYNTH_0_4;
+ break;
+ case 2:
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_XTAL;
+ break;
+ case 3:
+ if (pdata->variant != SI5351_VARIANT_C) {
+ dev_err(&client->dev,
+ "invalid parent %d for clkout %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_CLKIN;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid parent %d for clkout %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "silabs,drive-strength",
+ &val)) {
+ switch (val) {
+ case SI5351_DRIVE_2MA:
+ case SI5351_DRIVE_4MA:
+ case SI5351_DRIVE_6MA:
+ case SI5351_DRIVE_8MA:
+ pdata->clkout[num].drive = val;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid drive strength %d for clkout %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "clock-frequency", &val))
+ pdata->clkout[num].rate = val;
+
+ pdata->clkout[num].pll_master =
+ of_property_read_bool(child, "silabs,pll-master");
+ }
+ client->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int si5351_dt_parse(struct i2c_client *client)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static int si5351_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct si5351_platform_data *pdata;
+ struct si5351_driver_data *drvdata;
+ struct clk_init_data init;
+ struct clk *clk;
+ const char *parent_names[4];
+ u8 num_parents, num_clocks;
+ int ret, n;
+
+ ret = si5351_dt_parse(client);
+ if (ret)
+ return ret;
+
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (drvdata == NULL) {
+ dev_err(&client->dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(client, drvdata);
+ drvdata->client = client;
+ drvdata->variant = pdata->variant;
+ drvdata->pxtal = pdata->clk_xtal;
+ drvdata->pclkin = pdata->clk_clkin;
+
+ drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
+ if (IS_ERR(drvdata->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(drvdata->regmap);
+ }
+
+ /* Disable interrupts */
+ si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0);
+ /* Set disabled output drivers to drive low */
+ si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00);
+ si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00);
+ /* Ensure pll select is on XTAL for Si5351A/B */
+ if (drvdata->variant != SI5351_VARIANT_C)
+ si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+ SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0);
+
+ /* setup clock configuration */
+ for (n = 0; n < 2; n++) {
+ ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to reparent pll %d to %d\n",
+ n, pdata->pll_src[n]);
+ return ret;
+ }
+ }
+
+ for (n = 0; n < 8; n++) {
+ ret = _si5351_msynth_reparent(drvdata, n,
+ pdata->clkout[n].multisynth_src);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to reparent multisynth %d to %d\n",
+ n, pdata->clkout[n].multisynth_src);
+ return ret;
+ }
+
+ ret = _si5351_clkout_reparent(drvdata, n,
+ pdata->clkout[n].clkout_src);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to reparent clkout %d to %d\n",
+ n, pdata->clkout[n].clkout_src);
+ return ret;
+ }
+
+ ret = _si5351_clkout_set_drive_strength(drvdata, n,
+ pdata->clkout[n].drive);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed set drive strength of clkout%d to %d\n",
+ n, pdata->clkout[n].drive);
+ return ret;
+ }
+ }
+
+ /* register xtal input clock gate */
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_input_names[0];
+ init.ops = &si5351_xtal_ops;
+ init.flags = 0;
+ if (!IS_ERR(drvdata->pxtal)) {
+ drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
+ init.parent_names = &drvdata->pxtal_name;
+ init.num_parents = 1;
+ }
+ drvdata->xtal.init = &init;
+ clk = devm_clk_register(&client->dev, &drvdata->xtal);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ return PTR_ERR(clk);
+ }
+
+ /* register clkin input clock gate */
+ if (drvdata->variant == SI5351_VARIANT_C) {
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_input_names[1];
+ init.ops = &si5351_clkin_ops;
+ if (!IS_ERR(drvdata->pclkin)) {
+ drvdata->pclkin_name = __clk_get_name(drvdata->pclkin);
+ init.parent_names = &drvdata->pclkin_name;
+ init.num_parents = 1;
+ }
+ drvdata->clkin.init = &init;
+ clk = devm_clk_register(&client->dev, &drvdata->clkin);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ return PTR_ERR(clk);
+ }
+ }
+
+ /* Si5351C allows to mux either xtal or clkin to PLL input */
+ num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1;
+ parent_names[0] = si5351_input_names[0];
+ parent_names[1] = si5351_input_names[1];
+
+ /* register PLLA */
+ drvdata->pll[0].num = 0;
+ drvdata->pll[0].drvdata = drvdata;
+ drvdata->pll[0].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_pll_names[0];
+ init.ops = &si5351_pll_ops;
+ init.flags = 0;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ return -EINVAL;
+ }
+
+ /* register PLLB or VXCO (Si5351B) */
+ drvdata->pll[1].num = 1;
+ drvdata->pll[1].drvdata = drvdata;
+ drvdata->pll[1].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ if (drvdata->variant == SI5351_VARIANT_B) {
+ init.name = si5351_pll_names[2];
+ init.ops = &si5351_vxco_ops;
+ init.flags = CLK_IS_ROOT;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ } else {
+ init.name = si5351_pll_names[1];
+ init.ops = &si5351_pll_ops;
+ init.flags = 0;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ }
+ clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ return -EINVAL;
+ }
+
+ /* register clk multisync and clk out divider */
+ num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8;
+ parent_names[0] = si5351_pll_names[0];
+ if (drvdata->variant == SI5351_VARIANT_B)
+ parent_names[1] = si5351_pll_names[2];
+ else
+ parent_names[1] = si5351_pll_names[1];
+
+ drvdata->msynth = devm_kzalloc(&client->dev, num_clocks *
+ sizeof(*drvdata->msynth), GFP_KERNEL);
+ drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
+ sizeof(*drvdata->clkout), GFP_KERNEL);
+
+ drvdata->onecell.clk_num = num_clocks;
+ drvdata->onecell.clks = devm_kzalloc(&client->dev,
+ num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
+
+ if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
+ !drvdata->onecell.clks))
+ return -ENOMEM;
+
+ for (n = 0; n < num_clocks; n++) {
+ drvdata->msynth[n].num = n;
+ drvdata->msynth[n].drvdata = drvdata;
+ drvdata->msynth[n].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_msynth_names[n];
+ init.ops = &si5351_msynth_ops;
+ init.flags = 0;
+ if (pdata->clkout[n].pll_master)
+ init.flags |= CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = 2;
+ clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ return -EINVAL;
+ }
+ }
+
+ num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3;
+ parent_names[2] = si5351_input_names[0];
+ parent_names[3] = si5351_input_names[1];
+ for (n = 0; n < num_clocks; n++) {
+ parent_names[0] = si5351_msynth_names[n];
+ parent_names[1] = (n < 4) ? si5351_msynth_names[0] :
+ si5351_msynth_names[4];
+
+ drvdata->clkout[n].num = n;
+ drvdata->clkout[n].drvdata = drvdata;
+ drvdata->clkout[n].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_clkout_names[n];
+ init.ops = &si5351_clkout_ops;
+ init.flags = 0;
+ if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N)
+ init.flags |= CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ return -EINVAL;
+ }
+ drvdata->onecell.clks[n] = clk;
+
+ /* set initial clkout rate */
+ if (pdata->clkout[n].rate != 0) {
+ int ret;
+ ret = clk_set_rate(clk, pdata->clkout[n].rate);
+ if (ret != 0) {
+ dev_err(&client->dev, "Cannot set rate : %d\n",
+ ret);
+ }
+ }
+ }
+
+ ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
+ &drvdata->onecell);
+ if (ret) {
+ dev_err(&client->dev, "unable to add clk provider\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id si5351_i2c_ids[] = {
+ { "silabs,si5351", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
+
+static struct i2c_driver si5351_driver = {
+ .driver = {
+ .name = "si5351",
+ .of_match_table = of_match_ptr(si5351_dt_ids),
+ },
+ .probe = si5351_i2c_probe,
+ .id_table = si5351_i2c_ids,
+};
+module_i2c_driver(si5351_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com");
+MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h
new file mode 100644
index 000000000..af41b5080
--- /dev/null
+++ b/drivers/clk/clk-si5351.h
@@ -0,0 +1,155 @@
+/*
+ * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.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.
+ */
+
+#ifndef _CLK_SI5351_H_
+#define _CLK_SI5351_H_
+
+#define SI5351_BUS_BASE_ADDR 0x60
+
+#define SI5351_PLL_VCO_MIN 600000000
+#define SI5351_PLL_VCO_MAX 900000000
+#define SI5351_MULTISYNTH_MIN_FREQ 1000000
+#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000
+#define SI5351_MULTISYNTH_MAX_FREQ 160000000
+#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ
+#define SI5351_CLKOUT_MIN_FREQ 8000
+#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ
+#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ
+
+#define SI5351_PLL_A_MIN 15
+#define SI5351_PLL_A_MAX 90
+#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1)
+#define SI5351_PLL_C_MAX 1048575
+#define SI5351_MULTISYNTH_A_MIN 6
+#define SI5351_MULTISYNTH_A_MAX 1800
+#define SI5351_MULTISYNTH67_A_MAX 254
+#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1)
+#define SI5351_MULTISYNTH_C_MAX 1048575
+#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1)
+#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1)
+#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1)
+
+#define SI5351_DEVICE_STATUS 0
+#define SI5351_INTERRUPT_STATUS 1
+#define SI5351_INTERRUPT_MASK 2
+#define SI5351_STATUS_SYS_INIT (1<<7)
+#define SI5351_STATUS_LOL_B (1<<6)
+#define SI5351_STATUS_LOL_A (1<<5)
+#define SI5351_STATUS_LOS (1<<4)
+#define SI5351_OUTPUT_ENABLE_CTRL 3
+#define SI5351_OEB_PIN_ENABLE_CTRL 9
+#define SI5351_PLL_INPUT_SOURCE 15
+#define SI5351_CLKIN_DIV_MASK (3<<6)
+#define SI5351_CLKIN_DIV_1 (0<<6)
+#define SI5351_CLKIN_DIV_2 (1<<6)
+#define SI5351_CLKIN_DIV_4 (2<<6)
+#define SI5351_CLKIN_DIV_8 (3<<6)
+#define SI5351_PLLB_SOURCE (1<<3)
+#define SI5351_PLLA_SOURCE (1<<2)
+
+#define SI5351_CLK0_CTRL 16
+#define SI5351_CLK1_CTRL 17
+#define SI5351_CLK2_CTRL 18
+#define SI5351_CLK3_CTRL 19
+#define SI5351_CLK4_CTRL 20
+#define SI5351_CLK5_CTRL 21
+#define SI5351_CLK6_CTRL 22
+#define SI5351_CLK7_CTRL 23
+#define SI5351_CLK_POWERDOWN (1<<7)
+#define SI5351_CLK_INTEGER_MODE (1<<6)
+#define SI5351_CLK_PLL_SELECT (1<<5)
+#define SI5351_CLK_INVERT (1<<4)
+#define SI5351_CLK_INPUT_MASK (3<<2)
+#define SI5351_CLK_INPUT_XTAL (0<<2)
+#define SI5351_CLK_INPUT_CLKIN (1<<2)
+#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
+#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
+#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
+
+#define SI5351_CLK3_0_DISABLE_STATE 24
+#define SI5351_CLK7_4_DISABLE_STATE 25
+#define SI5351_CLK_DISABLE_STATE_LOW 0
+#define SI5351_CLK_DISABLE_STATE_HIGH 1
+#define SI5351_CLK_DISABLE_STATE_FLOAT 2
+#define SI5351_CLK_DISABLE_STATE_NEVER 3
+
+#define SI5351_PARAMETERS_LENGTH 8
+#define SI5351_PLLA_PARAMETERS 26
+#define SI5351_PLLB_PARAMETERS 34
+#define SI5351_CLK0_PARAMETERS 42
+#define SI5351_CLK1_PARAMETERS 50
+#define SI5351_CLK2_PARAMETERS 58
+#define SI5351_CLK3_PARAMETERS 66
+#define SI5351_CLK4_PARAMETERS 74
+#define SI5351_CLK5_PARAMETERS 82
+#define SI5351_CLK6_PARAMETERS 90
+#define SI5351_CLK7_PARAMETERS 91
+#define SI5351_CLK6_7_OUTPUT_DIVIDER 92
+#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4)
+#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0)
+#define SI5351_OUTPUT_CLK_DIV_SHIFT 4
+#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0
+#define SI5351_OUTPUT_CLK_DIV_1 0
+#define SI5351_OUTPUT_CLK_DIV_2 1
+#define SI5351_OUTPUT_CLK_DIV_4 2
+#define SI5351_OUTPUT_CLK_DIV_8 3
+#define SI5351_OUTPUT_CLK_DIV_16 4
+#define SI5351_OUTPUT_CLK_DIV_32 5
+#define SI5351_OUTPUT_CLK_DIV_64 6
+#define SI5351_OUTPUT_CLK_DIV_128 7
+#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2)
+
+#define SI5351_SSC_PARAM0 149
+#define SI5351_SSC_PARAM1 150
+#define SI5351_SSC_PARAM2 151
+#define SI5351_SSC_PARAM3 152
+#define SI5351_SSC_PARAM4 153
+#define SI5351_SSC_PARAM5 154
+#define SI5351_SSC_PARAM6 155
+#define SI5351_SSC_PARAM7 156
+#define SI5351_SSC_PARAM8 157
+#define SI5351_SSC_PARAM9 158
+#define SI5351_SSC_PARAM10 159
+#define SI5351_SSC_PARAM11 160
+#define SI5351_SSC_PARAM12 161
+
+#define SI5351_VXCO_PARAMETERS_LOW 162
+#define SI5351_VXCO_PARAMETERS_MID 163
+#define SI5351_VXCO_PARAMETERS_HIGH 164
+
+#define SI5351_CLK0_PHASE_OFFSET 165
+#define SI5351_CLK1_PHASE_OFFSET 166
+#define SI5351_CLK2_PHASE_OFFSET 167
+#define SI5351_CLK3_PHASE_OFFSET 168
+#define SI5351_CLK4_PHASE_OFFSET 169
+#define SI5351_CLK5_PHASE_OFFSET 170
+
+#define SI5351_PLL_RESET 177
+#define SI5351_PLL_RESET_B (1<<7)
+#define SI5351_PLL_RESET_A (1<<5)
+
+#define SI5351_CRYSTAL_LOAD 183
+#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
+#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
+#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
+#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
+
+#define SI5351_FANOUT_ENABLE 187
+#define SI5351_CLKIN_ENABLE (1<<7)
+#define SI5351_XTAL_ENABLE (1<<6)
+#define SI5351_MULTISYNTH_ENABLE (1<<4)
+
+#endif
diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
new file mode 100644
index 000000000..3af729b1b
--- /dev/null
+++ b/drivers/clk/clk-twl6040.c
@@ -0,0 +1,126 @@
+/*
+* TWL6040 clock module driver for OMAP4 McPDM functional clock
+*
+* Copyright (C) 2012 Texas Instruments Inc.
+* Peter Ujfalusi <peter.ujfalusi@ti.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.
+*
+* 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.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA
+*
+*/
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/clk-provider.h>
+
+struct twl6040_clk {
+ struct twl6040 *twl6040;
+ struct device *dev;
+ struct clk_hw mcpdm_fclk;
+ struct clk *clk;
+ int enabled;
+};
+
+static int twl6040_bitclk_is_enabled(struct clk_hw *hw)
+{
+ struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
+ mcpdm_fclk);
+ return twl6040_clk->enabled;
+}
+
+static int twl6040_bitclk_prepare(struct clk_hw *hw)
+{
+ struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
+ mcpdm_fclk);
+ int ret;
+
+ ret = twl6040_power(twl6040_clk->twl6040, 1);
+ if (!ret)
+ twl6040_clk->enabled = 1;
+
+ return ret;
+}
+
+static void twl6040_bitclk_unprepare(struct clk_hw *hw)
+{
+ struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
+ mcpdm_fclk);
+ int ret;
+
+ ret = twl6040_power(twl6040_clk->twl6040, 0);
+ if (!ret)
+ twl6040_clk->enabled = 0;
+}
+
+static const struct clk_ops twl6040_mcpdm_ops = {
+ .is_enabled = twl6040_bitclk_is_enabled,
+ .prepare = twl6040_bitclk_prepare,
+ .unprepare = twl6040_bitclk_unprepare,
+};
+
+static struct clk_init_data wm831x_clkout_init = {
+ .name = "mcpdm_fclk",
+ .ops = &twl6040_mcpdm_ops,
+ .flags = CLK_IS_ROOT,
+};
+
+static int twl6040_clk_probe(struct platform_device *pdev)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent);
+ struct twl6040_clk *clkdata;
+
+ clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
+ if (!clkdata)
+ return -ENOMEM;
+
+ clkdata->dev = &pdev->dev;
+ clkdata->twl6040 = twl6040;
+
+ clkdata->mcpdm_fclk.init = &wm831x_clkout_init;
+ clkdata->clk = clk_register(&pdev->dev, &clkdata->mcpdm_fclk);
+ if (IS_ERR(clkdata->clk))
+ return PTR_ERR(clkdata->clk);
+
+ dev_set_drvdata(&pdev->dev, clkdata);
+
+ return 0;
+}
+
+static int twl6040_clk_remove(struct platform_device *pdev)
+{
+ struct twl6040_clk *clkdata = dev_get_drvdata(&pdev->dev);
+
+ clk_unregister(clkdata->clk);
+
+ return 0;
+}
+
+static struct platform_driver twl6040_clk_driver = {
+ .driver = {
+ .name = "twl6040-clk",
+ .owner = THIS_MODULE,
+ },
+ .probe = twl6040_clk_probe,
+ .remove = twl6040_clk_remove,
+};
+
+module_platform_driver(twl6040_clk_driver);
+
+MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_ALIAS("platform:twl6040-clk");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c
new file mode 100644
index 000000000..a15f7928f
--- /dev/null
+++ b/drivers/clk/clk-u300.c
@@ -0,0 +1,746 @@
+/*
+ * U300 clock implementation
+ * Copyright (C) 2007-2012 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <mach/syscon.h>
+
+/*
+ * The clocking hierarchy currently looks like this.
+ * NOTE: the idea is NOT to show how the clocks are routed on the chip!
+ * The ideas is to show dependencies, so a clock higher up in the
+ * hierarchy has to be on in order for another clock to be on. Now,
+ * both CPU and DMA can actually be on top of the hierarchy, and that
+ * is not modeled currently. Instead we have the backbone AMBA bus on
+ * top. This bus cannot be programmed in any way but conceptually it
+ * needs to be active for the bridges and devices to transport data.
+ *
+ * Please be aware that a few clocks are hw controlled, which mean that
+ * the hw itself can turn on/off or change the rate of the clock when
+ * needed!
+ *
+ * AMBA bus
+ * |
+ * +- CPU
+ * +- FSMC NANDIF NAND Flash interface
+ * +- SEMI Shared Memory interface
+ * +- ISP Image Signal Processor (U335 only)
+ * +- CDS (U335 only)
+ * +- DMA Direct Memory Access Controller
+ * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL)
+ * +- APEX
+ * +- VIDEO_ENC AVE2/3 Video Encoder
+ * +- XGAM Graphics Accelerator Controller
+ * +- AHB
+ * |
+ * +- ahb:0 AHB Bridge
+ * | |
+ * | +- ahb:1 INTCON Interrupt controller
+ * | +- ahb:3 MSPRO Memory Stick Pro controller
+ * | +- ahb:4 EMIF External Memory interface
+ * |
+ * +- fast:0 FAST bridge
+ * | |
+ * | +- fast:1 MMCSD MMC/SD card reader controller
+ * | +- fast:2 I2S0 PCM I2S channel 0 controller
+ * | +- fast:3 I2S1 PCM I2S channel 1 controller
+ * | +- fast:4 I2C0 I2C channel 0 controller
+ * | +- fast:5 I2C1 I2C channel 1 controller
+ * | +- fast:6 SPI SPI controller
+ * | +- fast:7 UART1 Secondary UART (U335 only)
+ * |
+ * +- slow:0 SLOW bridge
+ * |
+ * +- slow:1 SYSCON (not possible to control)
+ * +- slow:2 WDOG Watchdog
+ * +- slow:3 UART0 primary UART
+ * +- slow:4 TIMER_APP Application timer - used in Linux
+ * +- slow:5 KEYPAD controller
+ * +- slow:6 GPIO controller
+ * +- slow:7 RTC controller
+ * +- slow:8 BT Bus Tracer (not used currently)
+ * +- slow:9 EH Event Handler (not used currently)
+ * +- slow:a TIMER_ACC Access style timer (not used currently)
+ * +- slow:b PPM (U335 only, what is that?)
+ */
+
+/* Global syscon virtual base */
+static void __iomem *syscon_vbase;
+
+/**
+ * struct clk_syscon - U300 syscon clock
+ * @hw: corresponding clock hardware entry
+ * @hw_ctrld: whether this clock is hardware controlled (for refcount etc)
+ * and does not need any magic pokes to be enabled/disabled
+ * @reset: state holder, whether this block's reset line is asserted or not
+ * @res_reg: reset line enable/disable flag register
+ * @res_bit: bit for resetting or taking this consumer out of reset
+ * @en_reg: clock line enable/disable flag register
+ * @en_bit: bit for enabling/disabling this consumer clock line
+ * @clk_val: magic value to poke in the register to enable/disable
+ * this one clock
+ */
+struct clk_syscon {
+ struct clk_hw hw;
+ bool hw_ctrld;
+ bool reset;
+ void __iomem *res_reg;
+ u8 res_bit;
+ void __iomem *en_reg;
+ u8 en_bit;
+ u16 clk_val;
+};
+
+#define to_syscon(_hw) container_of(_hw, struct clk_syscon, hw)
+
+static DEFINE_SPINLOCK(syscon_resetreg_lock);
+
+/*
+ * Reset control functions. We remember if a block has been
+ * taken out of reset and don't remove the reset assertion again
+ * and vice versa. Currently we only remove resets so the
+ * enablement function is defined out.
+ */
+static void syscon_block_reset_enable(struct clk_syscon *sclk)
+{
+ unsigned long iflags;
+ u16 val;
+
+ /* Not all blocks support resetting */
+ if (!sclk->res_reg)
+ return;
+ spin_lock_irqsave(&syscon_resetreg_lock, iflags);
+ val = readw(sclk->res_reg);
+ val |= BIT(sclk->res_bit);
+ writew(val, sclk->res_reg);
+ spin_unlock_irqrestore(&syscon_resetreg_lock, iflags);
+ sclk->reset = true;
+}
+
+static void syscon_block_reset_disable(struct clk_syscon *sclk)
+{
+ unsigned long iflags;
+ u16 val;
+
+ /* Not all blocks support resetting */
+ if (!sclk->res_reg)
+ return;
+ spin_lock_irqsave(&syscon_resetreg_lock, iflags);
+ val = readw(sclk->res_reg);
+ val &= ~BIT(sclk->res_bit);
+ writew(val, sclk->res_reg);
+ spin_unlock_irqrestore(&syscon_resetreg_lock, iflags);
+ sclk->reset = false;
+}
+
+static int syscon_clk_prepare(struct clk_hw *hw)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+
+ /* If the block is in reset, bring it out */
+ if (sclk->reset)
+ syscon_block_reset_disable(sclk);
+ return 0;
+}
+
+static void syscon_clk_unprepare(struct clk_hw *hw)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+
+ /* Please don't force the console into reset */
+ if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN)
+ return;
+ /* When unpreparing, force block into reset */
+ if (!sclk->reset)
+ syscon_block_reset_enable(sclk);
+}
+
+static int syscon_clk_enable(struct clk_hw *hw)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+
+ /* Don't touch the hardware controlled clocks */
+ if (sclk->hw_ctrld)
+ return 0;
+ /* These cannot be controlled */
+ if (sclk->clk_val == 0xFFFFU)
+ return 0;
+
+ writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCER);
+ return 0;
+}
+
+static void syscon_clk_disable(struct clk_hw *hw)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+
+ /* Don't touch the hardware controlled clocks */
+ if (sclk->hw_ctrld)
+ return;
+ if (sclk->clk_val == 0xFFFFU)
+ return;
+ /* Please don't disable the console port */
+ if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN)
+ return;
+
+ writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCDR);
+}
+
+static int syscon_clk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+ u16 val;
+
+ /* If no enable register defined, it's always-on */
+ if (!sclk->en_reg)
+ return 1;
+
+ val = readw(sclk->en_reg);
+ val &= BIT(sclk->en_bit);
+
+ return val ? 1 : 0;
+}
+
+static u16 syscon_get_perf(void)
+{
+ u16 val;
+
+ val = readw(syscon_vbase + U300_SYSCON_CCR);
+ val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK;
+ return val;
+}
+
+static unsigned long
+syscon_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+ u16 perf = syscon_get_perf();
+
+ switch(sclk->clk_val) {
+ case U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN:
+ case U300_SYSCON_SBCER_I2C0_CLK_EN:
+ case U300_SYSCON_SBCER_I2C1_CLK_EN:
+ case U300_SYSCON_SBCER_MMC_CLK_EN:
+ case U300_SYSCON_SBCER_SPI_CLK_EN:
+ /* The FAST clocks have one progression */
+ switch(perf) {
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
+ return 13000000;
+ default:
+ return parent_rate; /* 26 MHz */
+ }
+ case U300_SYSCON_SBCER_DMAC_CLK_EN:
+ case U300_SYSCON_SBCER_NANDIF_CLK_EN:
+ case U300_SYSCON_SBCER_XGAM_CLK_EN:
+ /* AMBA interconnect peripherals */
+ switch(perf) {
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
+ return 6500000;
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
+ return 26000000;
+ default:
+ return parent_rate; /* 52 MHz */
+ }
+ case U300_SYSCON_SBCER_SEMI_CLK_EN:
+ case U300_SYSCON_SBCER_EMIF_CLK_EN:
+ /* EMIF speeds */
+ switch(perf) {
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
+ return 13000000;
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
+ return 52000000;
+ default:
+ return 104000000;
+ }
+ case U300_SYSCON_SBCER_CPU_CLK_EN:
+ /* And the fast CPU clock */
+ switch(perf) {
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
+ return 13000000;
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
+ return 52000000;
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH:
+ return 104000000;
+ default:
+ return parent_rate; /* 208 MHz */
+ }
+ default:
+ /*
+ * The SLOW clocks and default just inherit the rate of
+ * their parent (typically PLL13 13 MHz).
+ */
+ return parent_rate;
+ }
+}
+
+static long
+syscon_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+
+ if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN)
+ return *prate;
+ /* We really only support setting the rate of the CPU clock */
+ if (rate <= 13000000)
+ return 13000000;
+ if (rate <= 52000000)
+ return 52000000;
+ if (rate <= 104000000)
+ return 104000000;
+ return 208000000;
+}
+
+static int syscon_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_syscon *sclk = to_syscon(hw);
+ u16 val;
+
+ /* We only support setting the rate of the CPU clock */
+ if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN)
+ return -EINVAL;
+ switch (rate) {
+ case 13000000:
+ val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER;
+ break;
+ case 52000000:
+ val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE;
+ break;
+ case 104000000:
+ val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH;
+ break;
+ case 208000000:
+ val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val |= readw(syscon_vbase + U300_SYSCON_CCR) &
+ ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ;
+ writew(val, syscon_vbase + U300_SYSCON_CCR);
+ return 0;
+}
+
+static const struct clk_ops syscon_clk_ops = {
+ .prepare = syscon_clk_prepare,
+ .unprepare = syscon_clk_unprepare,
+ .enable = syscon_clk_enable,
+ .disable = syscon_clk_disable,
+ .is_enabled = syscon_clk_is_enabled,
+ .recalc_rate = syscon_clk_recalc_rate,
+ .round_rate = syscon_clk_round_rate,
+ .set_rate = syscon_clk_set_rate,
+};
+
+static struct clk * __init
+syscon_clk_register(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ bool hw_ctrld,
+ void __iomem *res_reg, u8 res_bit,
+ void __iomem *en_reg, u8 en_bit,
+ u16 clk_val)
+{
+ struct clk *clk;
+ struct clk_syscon *sclk;
+ struct clk_init_data init;
+
+ sclk = kzalloc(sizeof(struct clk_syscon), GFP_KERNEL);
+ if (!sclk) {
+ pr_err("could not allocate syscon clock %s\n",
+ name);
+ return ERR_PTR(-ENOMEM);
+ }
+ init.name = name;
+ init.ops = &syscon_clk_ops;
+ init.flags = flags;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ sclk->hw.init = &init;
+ sclk->hw_ctrld = hw_ctrld;
+ /* Assume the block is in reset at registration */
+ sclk->reset = true;
+ sclk->res_reg = res_reg;
+ sclk->res_bit = res_bit;
+ sclk->en_reg = en_reg;
+ sclk->en_bit = en_bit;
+ sclk->clk_val = clk_val;
+
+ clk = clk_register(dev, &sclk->hw);
+ if (IS_ERR(clk))
+ kfree(sclk);
+
+ return clk;
+}
+
+/**
+ * struct clk_mclk - U300 MCLK clock (MMC/SD clock)
+ * @hw: corresponding clock hardware entry
+ * @is_mspro: if this is the memory stick clock rather than MMC/SD
+ */
+struct clk_mclk {
+ struct clk_hw hw;
+ bool is_mspro;
+};
+
+#define to_mclk(_hw) container_of(_hw, struct clk_mclk, hw)
+
+static int mclk_clk_prepare(struct clk_hw *hw)
+{
+ struct clk_mclk *mclk = to_mclk(hw);
+ u16 val;
+
+ /* The MMC and MSPRO clocks need some special set-up */
+ if (!mclk->is_mspro) {
+ /* Set default MMC clock divisor to 18.9 MHz */
+ writew(0x0054U, syscon_vbase + U300_SYSCON_MMF0R);
+ val = readw(syscon_vbase + U300_SYSCON_MMCR);
+ /* Disable the MMC feedback clock */
+ val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE;
+ /* Disable MSPRO frequency */
+ val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE;
+ writew(val, syscon_vbase + U300_SYSCON_MMCR);
+ } else {
+ val = readw(syscon_vbase + U300_SYSCON_MMCR);
+ /* Disable the MMC feedback clock */
+ val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE;
+ /* Enable MSPRO frequency */
+ val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE;
+ writew(val, syscon_vbase + U300_SYSCON_MMCR);
+ }
+
+ return 0;
+}
+
+static unsigned long
+mclk_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u16 perf = syscon_get_perf();
+
+ switch (perf) {
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
+ /*
+ * Here, the 208 MHz PLL gets shut down and the always
+ * on 13 MHz PLL used for RTC etc kicks into use
+ * instead.
+ */
+ return 13000000;
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH:
+ case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST:
+ {
+ /*
+ * This clock is under program control. The register is
+ * divided in two nybbles, bit 7-4 gives cycles-1 to count
+ * high, bit 3-0 gives cycles-1 to count low. Distribute
+ * these with no more than 1 cycle difference between
+ * low and high and add low and high to get the actual
+ * divisor. The base PLL is 208 MHz. Writing 0x00 will
+ * divide by 1 and 1 so the highest frequency possible
+ * is 104 MHz.
+ *
+ * e.g. 0x54 =>
+ * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz
+ */
+ u16 val = readw(syscon_vbase + U300_SYSCON_MMF0R) &
+ U300_SYSCON_MMF0R_MASK;
+ switch (val) {
+ case 0x0054:
+ return 18900000;
+ case 0x0044:
+ return 20800000;
+ case 0x0043:
+ return 23100000;
+ case 0x0033:
+ return 26000000;
+ case 0x0032:
+ return 29700000;
+ case 0x0022:
+ return 34700000;
+ case 0x0021:
+ return 41600000;
+ case 0x0011:
+ return 52000000;
+ case 0x0000:
+ return 104000000;
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ return parent_rate;
+}
+
+static long
+mclk_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ if (rate <= 18900000)
+ return 18900000;
+ if (rate <= 20800000)
+ return 20800000;
+ if (rate <= 23100000)
+ return 23100000;
+ if (rate <= 26000000)
+ return 26000000;
+ if (rate <= 29700000)
+ return 29700000;
+ if (rate <= 34700000)
+ return 34700000;
+ if (rate <= 41600000)
+ return 41600000;
+ /* Highest rate */
+ return 52000000;
+}
+
+static int mclk_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ u16 val;
+ u16 reg;
+
+ switch (rate) {
+ case 18900000:
+ val = 0x0054;
+ break;
+ case 20800000:
+ val = 0x0044;
+ break;
+ case 23100000:
+ val = 0x0043;
+ break;
+ case 26000000:
+ val = 0x0033;
+ break;
+ case 29700000:
+ val = 0x0032;
+ break;
+ case 34700000:
+ val = 0x0022;
+ break;
+ case 41600000:
+ val = 0x0021;
+ break;
+ case 52000000:
+ val = 0x0011;
+ break;
+ case 104000000:
+ val = 0x0000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg = readw(syscon_vbase + U300_SYSCON_MMF0R) &
+ ~U300_SYSCON_MMF0R_MASK;
+ writew(reg | val, syscon_vbase + U300_SYSCON_MMF0R);
+ return 0;
+}
+
+static const struct clk_ops mclk_ops = {
+ .prepare = mclk_clk_prepare,
+ .recalc_rate = mclk_clk_recalc_rate,
+ .round_rate = mclk_clk_round_rate,
+ .set_rate = mclk_clk_set_rate,
+};
+
+static struct clk * __init
+mclk_clk_register(struct device *dev, const char *name,
+ const char *parent_name, bool is_mspro)
+{
+ struct clk *clk;
+ struct clk_mclk *mclk;
+ struct clk_init_data init;
+
+ mclk = kzalloc(sizeof(struct clk_mclk), GFP_KERNEL);
+ if (!mclk) {
+ pr_err("could not allocate MMC/SD clock %s\n",
+ name);
+ return ERR_PTR(-ENOMEM);
+ }
+ init.name = "mclk";
+ init.ops = &mclk_ops;
+ init.flags = 0;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ mclk->hw.init = &init;
+ mclk->is_mspro = is_mspro;
+
+ clk = clk_register(dev, &mclk->hw);
+ if (IS_ERR(clk))
+ kfree(mclk);
+
+ return clk;
+}
+
+void __init u300_clk_init(void __iomem *base)
+{
+ u16 val;
+ struct clk *clk;
+
+ syscon_vbase = base;
+
+ /* Set system to run at PLL208, max performance, a known state. */
+ val = readw(syscon_vbase + U300_SYSCON_CCR);
+ val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK;
+ writew(val, syscon_vbase + U300_SYSCON_CCR);
+ /* Wait for the PLL208 to lock if not locked in yet */
+ while (!(readw(syscon_vbase + U300_SYSCON_CSR) &
+ U300_SYSCON_CSR_PLL208_LOCK_IND));
+
+ /* Power management enable */
+ val = readw(syscon_vbase + U300_SYSCON_PMCR);
+ val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE;
+ writew(val, syscon_vbase + U300_SYSCON_PMCR);
+
+ /* These are always available (RTC and PLL13) */
+ clk = clk_register_fixed_rate(NULL, "app_32_clk", NULL,
+ CLK_IS_ROOT, 32768);
+ /* The watchdog sits directly on the 32 kHz clock */
+ clk_register_clkdev(clk, NULL, "coh901327_wdog");
+ clk = clk_register_fixed_rate(NULL, "pll13", NULL,
+ CLK_IS_ROOT, 13000000);
+
+ /* These derive from PLL208 */
+ clk = clk_register_fixed_rate(NULL, "pll208", NULL,
+ CLK_IS_ROOT, 208000000);
+ clk = clk_register_fixed_factor(NULL, "app_208_clk", "pll208",
+ 0, 1, 1);
+ clk = clk_register_fixed_factor(NULL, "app_104_clk", "pll208",
+ 0, 1, 2);
+ clk = clk_register_fixed_factor(NULL, "app_52_clk", "pll208",
+ 0, 1, 4);
+ /* The 52 MHz is divided down to 26 MHz */
+ clk = clk_register_fixed_factor(NULL, "app_26_clk", "app_52_clk",
+ 0, 1, 2);
+
+ /* Directly on the AMBA interconnect */
+ clk = syscon_clk_register(NULL, "cpu_clk", "app_208_clk", 0, true,
+ syscon_vbase + U300_SYSCON_RRR, 3,
+ syscon_vbase + U300_SYSCON_CERR, 3,
+ U300_SYSCON_SBCER_CPU_CLK_EN);
+ clk = syscon_clk_register(NULL, "dmac_clk", "app_52_clk", 0, true,
+ syscon_vbase + U300_SYSCON_RRR, 4,
+ syscon_vbase + U300_SYSCON_CERR, 4,
+ U300_SYSCON_SBCER_DMAC_CLK_EN);
+ clk_register_clkdev(clk, NULL, "dma");
+ clk = syscon_clk_register(NULL, "fsmc_clk", "app_52_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RRR, 6,
+ syscon_vbase + U300_SYSCON_CERR, 6,
+ U300_SYSCON_SBCER_NANDIF_CLK_EN);
+ clk_register_clkdev(clk, NULL, "fsmc-nand");
+ clk = syscon_clk_register(NULL, "xgam_clk", "app_52_clk", 0, true,
+ syscon_vbase + U300_SYSCON_RRR, 8,
+ syscon_vbase + U300_SYSCON_CERR, 8,
+ U300_SYSCON_SBCER_XGAM_CLK_EN);
+ clk_register_clkdev(clk, NULL, "xgam");
+ clk = syscon_clk_register(NULL, "semi_clk", "app_104_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RRR, 9,
+ syscon_vbase + U300_SYSCON_CERR, 9,
+ U300_SYSCON_SBCER_SEMI_CLK_EN);
+ clk_register_clkdev(clk, NULL, "semi");
+
+ /* AHB bridge clocks */
+ clk = syscon_clk_register(NULL, "ahb_subsys_clk", "app_52_clk", 0, true,
+ syscon_vbase + U300_SYSCON_RRR, 10,
+ syscon_vbase + U300_SYSCON_CERR, 10,
+ U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN);
+ clk = syscon_clk_register(NULL, "intcon_clk", "ahb_subsys_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RRR, 12,
+ syscon_vbase + U300_SYSCON_CERR, 12,
+ /* Cannot be enabled, just taken out of reset */
+ 0xFFFFU);
+ clk_register_clkdev(clk, NULL, "intcon");
+ clk = syscon_clk_register(NULL, "emif_clk", "ahb_subsys_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RRR, 5,
+ syscon_vbase + U300_SYSCON_CERR, 5,
+ U300_SYSCON_SBCER_EMIF_CLK_EN);
+ clk_register_clkdev(clk, NULL, "pl172");
+
+ /* FAST bridge clocks */
+ clk = syscon_clk_register(NULL, "fast_clk", "app_26_clk", 0, true,
+ syscon_vbase + U300_SYSCON_RFR, 0,
+ syscon_vbase + U300_SYSCON_CEFR, 0,
+ U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN);
+ clk = syscon_clk_register(NULL, "i2c0_p_clk", "fast_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RFR, 1,
+ syscon_vbase + U300_SYSCON_CEFR, 1,
+ U300_SYSCON_SBCER_I2C0_CLK_EN);
+ clk_register_clkdev(clk, NULL, "stu300.0");
+ clk = syscon_clk_register(NULL, "i2c1_p_clk", "fast_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RFR, 2,
+ syscon_vbase + U300_SYSCON_CEFR, 2,
+ U300_SYSCON_SBCER_I2C1_CLK_EN);
+ clk_register_clkdev(clk, NULL, "stu300.1");
+ clk = syscon_clk_register(NULL, "mmc_p_clk", "fast_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RFR, 5,
+ syscon_vbase + U300_SYSCON_CEFR, 5,
+ U300_SYSCON_SBCER_MMC_CLK_EN);
+ clk_register_clkdev(clk, "apb_pclk", "mmci");
+ clk = syscon_clk_register(NULL, "spi_p_clk", "fast_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RFR, 6,
+ syscon_vbase + U300_SYSCON_CEFR, 6,
+ U300_SYSCON_SBCER_SPI_CLK_EN);
+ /* The SPI has no external clock for the outward bus, uses the pclk */
+ clk_register_clkdev(clk, NULL, "pl022");
+ clk_register_clkdev(clk, "apb_pclk", "pl022");
+
+ /* SLOW bridge clocks */
+ clk = syscon_clk_register(NULL, "slow_clk", "pll13", 0, true,
+ syscon_vbase + U300_SYSCON_RSR, 0,
+ syscon_vbase + U300_SYSCON_CESR, 0,
+ U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN);
+ clk = syscon_clk_register(NULL, "uart0_clk", "slow_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RSR, 1,
+ syscon_vbase + U300_SYSCON_CESR, 1,
+ U300_SYSCON_SBCER_UART_CLK_EN);
+ /* Same clock is used for APB and outward bus */
+ clk_register_clkdev(clk, NULL, "uart0");
+ clk_register_clkdev(clk, "apb_pclk", "uart0");
+ clk = syscon_clk_register(NULL, "gpio_clk", "slow_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RSR, 4,
+ syscon_vbase + U300_SYSCON_CESR, 4,
+ U300_SYSCON_SBCER_GPIO_CLK_EN);
+ clk_register_clkdev(clk, NULL, "u300-gpio");
+ clk = syscon_clk_register(NULL, "keypad_clk", "slow_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RSR, 5,
+ syscon_vbase + U300_SYSCON_CESR, 6,
+ U300_SYSCON_SBCER_KEYPAD_CLK_EN);
+ clk_register_clkdev(clk, NULL, "coh901461-keypad");
+ clk = syscon_clk_register(NULL, "rtc_clk", "slow_clk", 0, true,
+ syscon_vbase + U300_SYSCON_RSR, 6,
+ /* No clock enable register bit */
+ NULL, 0, 0xFFFFU);
+ clk_register_clkdev(clk, NULL, "rtc-coh901331");
+ clk = syscon_clk_register(NULL, "app_tmr_clk", "slow_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RSR, 7,
+ syscon_vbase + U300_SYSCON_CESR, 7,
+ U300_SYSCON_SBCER_APP_TMR_CLK_EN);
+ clk_register_clkdev(clk, NULL, "apptimer");
+ clk = syscon_clk_register(NULL, "acc_tmr_clk", "slow_clk", 0, false,
+ syscon_vbase + U300_SYSCON_RSR, 8,
+ syscon_vbase + U300_SYSCON_CESR, 8,
+ U300_SYSCON_SBCER_ACC_TMR_CLK_EN);
+ clk_register_clkdev(clk, NULL, "timer");
+
+ /* Then this special MMC/SD clock */
+ clk = mclk_clk_register(NULL, "mmc_clk", "mmc_p_clk", false);
+ clk_register_clkdev(clk, NULL, "mmci");
+}
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
new file mode 100644
index 000000000..553ac35bc
--- /dev/null
+++ b/drivers/clk/clk-vt8500.c
@@ -0,0 +1,639 @@
+/*
+ * Clock implementation for VIA/Wondermedia SoC's
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * 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/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+/* All clocks share the same lock as none can be changed concurrently */
+static DEFINE_SPINLOCK(_lock);
+
+struct clk_device {
+ struct clk_hw hw;
+ void __iomem *div_reg;
+ unsigned int div_mask;
+ void __iomem *en_reg;
+ int en_bit;
+ spinlock_t *lock;
+};
+
+/*
+ * Add new PLL_TYPE_x definitions here as required. Use the first known model
+ * to support the new type as the name.
+ * Add case statements to vtwm_pll_recalc_rate(), vtwm_pll_round_round() and
+ * vtwm_pll_set_rate() to handle the new PLL_TYPE_x
+ */
+
+#define PLL_TYPE_VT8500 0
+#define PLL_TYPE_WM8650 1
+#define PLL_TYPE_WM8750 2
+
+struct clk_pll {
+ struct clk_hw hw;
+ void __iomem *reg;
+ spinlock_t *lock;
+ int type;
+};
+
+static void __iomem *pmc_base;
+
+#define to_clk_device(_hw) container_of(_hw, struct clk_device, hw)
+
+#define VT8500_PMC_BUSY_MASK 0x18
+
+static void vt8500_pmc_wait_busy(void)
+{
+ while (readl(pmc_base) & VT8500_PMC_BUSY_MASK)
+ cpu_relax();
+}
+
+static int vt8500_dclk_enable(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ en_val = readl(cdev->en_reg);
+ en_val |= BIT(cdev->en_bit);
+ writel(en_val, cdev->en_reg);
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+ return 0;
+}
+
+static void vt8500_dclk_disable(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ en_val = readl(cdev->en_reg);
+ en_val &= ~BIT(cdev->en_bit);
+ writel(en_val, cdev->en_reg);
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+}
+
+static int vt8500_dclk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val = (readl(cdev->en_reg) & BIT(cdev->en_bit));
+
+ return en_val ? 1 : 0;
+}
+
+static unsigned long vt8500_dclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 div = readl(cdev->div_reg) & cdev->div_mask;
+
+ /* Special case for SDMMC devices */
+ if ((cdev->div_mask == 0x3F) && (div & BIT(5)))
+ div = 64 * (div & 0x1f);
+
+ /* div == 0 is actually the highest divisor */
+ if (div == 0)
+ div = (cdev->div_mask + 1);
+
+ return parent_rate / div;
+}
+
+static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 divisor;
+
+ if (rate == 0)
+ return 0;
+
+ divisor = *prate / rate;
+
+ /* If prate / rate would be decimal, incr the divisor */
+ if (rate * divisor < *prate)
+ divisor++;
+
+ /*
+ * If this is a request for SDMMC we have to adjust the divisor
+ * when >31 to use the fixed predivisor
+ */
+ if ((cdev->div_mask == 0x3F) && (divisor > 31)) {
+ divisor = 64 * ((divisor / 64) + 1);
+ }
+
+ return *prate / divisor;
+}
+
+static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 divisor;
+ unsigned long flags = 0;
+
+ if (rate == 0)
+ return 0;
+
+ divisor = parent_rate / rate;
+
+ /* If prate / rate would be decimal, incr the divisor */
+ if (rate * divisor < parent_rate)
+ divisor++;
+
+ if (divisor == cdev->div_mask + 1)
+ divisor = 0;
+
+ /* SDMMC mask may need to be corrected before testing if its valid */
+ if ((cdev->div_mask == 0x3F) && (divisor > 31)) {
+ /*
+ * Bit 5 is a fixed /64 predivisor. If the requested divisor
+ * is >31 then correct for the fixed divisor being required.
+ */
+ divisor = 0x20 + (divisor / 64);
+ }
+
+ if (divisor > cdev->div_mask) {
+ pr_err("%s: invalid divisor for clock\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ vt8500_pmc_wait_busy();
+ writel(divisor, cdev->div_reg);
+ vt8500_pmc_wait_busy();
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+
+ return 0;
+}
+
+
+static const struct clk_ops vt8500_gated_clk_ops = {
+ .enable = vt8500_dclk_enable,
+ .disable = vt8500_dclk_disable,
+ .is_enabled = vt8500_dclk_is_enabled,
+};
+
+static const struct clk_ops vt8500_divisor_clk_ops = {
+ .round_rate = vt8500_dclk_round_rate,
+ .set_rate = vt8500_dclk_set_rate,
+ .recalc_rate = vt8500_dclk_recalc_rate,
+};
+
+static const struct clk_ops vt8500_gated_divisor_clk_ops = {
+ .enable = vt8500_dclk_enable,
+ .disable = vt8500_dclk_disable,
+ .is_enabled = vt8500_dclk_is_enabled,
+ .round_rate = vt8500_dclk_round_rate,
+ .set_rate = vt8500_dclk_set_rate,
+ .recalc_rate = vt8500_dclk_recalc_rate,
+};
+
+#define CLK_INIT_GATED BIT(0)
+#define CLK_INIT_DIVISOR BIT(1)
+#define CLK_INIT_GATED_DIVISOR (CLK_INIT_DIVISOR | CLK_INIT_GATED)
+
+static __init void vtwm_device_clk_init(struct device_node *node)
+{
+ u32 en_reg, div_reg;
+ struct clk *clk;
+ struct clk_device *dev_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ int clk_init_flags = 0;
+
+ dev_clk = kzalloc(sizeof(*dev_clk), GFP_KERNEL);
+ if (WARN_ON(!dev_clk))
+ return;
+
+ dev_clk->lock = &_lock;
+
+ rc = of_property_read_u32(node, "enable-reg", &en_reg);
+ if (!rc) {
+ dev_clk->en_reg = pmc_base + en_reg;
+ rc = of_property_read_u32(node, "enable-bit", &dev_clk->en_bit);
+ if (rc) {
+ pr_err("%s: enable-bit property required for gated clock\n",
+ __func__);
+ return;
+ }
+ clk_init_flags |= CLK_INIT_GATED;
+ }
+
+ rc = of_property_read_u32(node, "divisor-reg", &div_reg);
+ if (!rc) {
+ dev_clk->div_reg = pmc_base + div_reg;
+ /*
+ * use 0x1f as the default mask since it covers
+ * almost all the clocks and reduces dts properties
+ */
+ dev_clk->div_mask = 0x1f;
+
+ of_property_read_u32(node, "divisor-mask", &dev_clk->div_mask);
+ clk_init_flags |= CLK_INIT_DIVISOR;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ switch (clk_init_flags) {
+ case CLK_INIT_GATED:
+ init.ops = &vt8500_gated_clk_ops;
+ break;
+ case CLK_INIT_DIVISOR:
+ init.ops = &vt8500_divisor_clk_ops;
+ break;
+ case CLK_INIT_GATED_DIVISOR:
+ init.ops = &vt8500_gated_divisor_clk_ops;
+ break;
+ default:
+ pr_err("%s: Invalid clock description in device tree\n",
+ __func__);
+ kfree(dev_clk);
+ return;
+ }
+
+ init.name = clk_name;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ dev_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &dev_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(dev_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+}
+CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init);
+
+/* PLL clock related functions */
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+/* Helper macros for PLL_VT8500 */
+#define VT8500_PLL_MUL(x) ((x & 0x1F) << 1)
+#define VT8500_PLL_DIV(x) ((x & 0x100) ? 1 : 2)
+
+#define VT8500_BITS_TO_FREQ(r, m, d) \
+ ((r / d) * m)
+
+#define VT8500_BITS_TO_VAL(m, d) \
+ ((d == 2 ? 0 : 0x100) | ((m >> 1) & 0x1F))
+
+/* Helper macros for PLL_WM8650 */
+#define WM8650_PLL_MUL(x) (x & 0x3FF)
+#define WM8650_PLL_DIV(x) (((x >> 10) & 7) * (1 << ((x >> 13) & 3)))
+
+#define WM8650_BITS_TO_FREQ(r, m, d1, d2) \
+ (r * m / (d1 * (1 << d2)))
+
+#define WM8650_BITS_TO_VAL(m, d1, d2) \
+ ((d2 << 13) | (d1 << 10) | (m & 0x3FF))
+
+/* Helper macros for PLL_WM8750 */
+#define WM8750_PLL_MUL(x) (((x >> 16) & 0xFF) + 1)
+#define WM8750_PLL_DIV(x) ((((x >> 8) & 1) + 1) * (1 << (x & 7)))
+
+#define WM8750_BITS_TO_FREQ(r, m, d1, d2) \
+ (r * (m+1) / ((d1+1) * (1 << d2)))
+
+#define WM8750_BITS_TO_VAL(f, m, d1, d2) \
+ ((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2)
+
+
+static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *multiplier, u32 *prediv)
+{
+ unsigned long tclk;
+
+ /* sanity check */
+ if ((rate < parent_rate * 4) || (rate > parent_rate * 62)) {
+ pr_err("%s: requested rate out of range\n", __func__);
+ *multiplier = 0;
+ *prediv = 1;
+ return;
+ }
+ if (rate <= parent_rate * 31)
+ /* use the prediv to double the resolution */
+ *prediv = 2;
+ else
+ *prediv = 1;
+
+ *multiplier = rate / (parent_rate / *prediv);
+ tclk = (parent_rate / *prediv) * *multiplier;
+
+ if (tclk != rate)
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__,
+ rate, tclk);
+}
+
+static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *multiplier, u32 *divisor1, u32 *divisor2)
+{
+ u32 mul, div1, div2;
+ u32 best_mul, best_div1, best_div2;
+ unsigned long tclk, rate_err, best_err;
+
+ best_err = (unsigned long)-1;
+
+ /* Find the closest match (lower or equal to requested) */
+ for (div1 = 5; div1 >= 3; div1--)
+ for (div2 = 3; div2 >= 0; div2--)
+ for (mul = 3; mul <= 1023; mul++) {
+ tclk = parent_rate * mul / (div1 * (1 << div2));
+ if (tclk > rate)
+ continue;
+ /* error will always be +ve */
+ rate_err = rate - tclk;
+ if (rate_err == 0) {
+ *multiplier = mul;
+ *divisor1 = div1;
+ *divisor2 = div2;
+ return;
+ }
+
+ if (rate_err < best_err) {
+ best_err = rate_err;
+ best_mul = mul;
+ best_div1 = div1;
+ best_div2 = div2;
+ }
+ }
+
+ /* if we got here, it wasn't an exact match */
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate,
+ rate - best_err);
+ *multiplier = best_mul;
+ *divisor1 = best_div1;
+ *divisor2 = best_div2;
+}
+
+static u32 wm8750_get_filter(u32 parent_rate, u32 divisor1)
+{
+ /* calculate frequency (MHz) after pre-divisor */
+ u32 freq = (parent_rate / 1000000) / (divisor1 + 1);
+
+ if ((freq < 10) || (freq > 200))
+ pr_warn("%s: PLL recommended input frequency 10..200Mhz (requested %d Mhz)\n",
+ __func__, freq);
+
+ if (freq >= 166)
+ return 7;
+ else if (freq >= 104)
+ return 6;
+ else if (freq >= 65)
+ return 5;
+ else if (freq >= 42)
+ return 4;
+ else if (freq >= 26)
+ return 3;
+ else if (freq >= 16)
+ return 2;
+ else if (freq >= 10)
+ return 1;
+
+ return 0;
+}
+
+static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *filter, u32 *multiplier, u32 *divisor1, u32 *divisor2)
+{
+ u32 mul, div1, div2;
+ u32 best_mul, best_div1, best_div2;
+ unsigned long tclk, rate_err, best_err;
+
+ best_err = (unsigned long)-1;
+
+ /* Find the closest match (lower or equal to requested) */
+ for (div1 = 1; div1 >= 0; div1--)
+ for (div2 = 7; div2 >= 0; div2--)
+ for (mul = 0; mul <= 255; mul++) {
+ tclk = parent_rate * (mul + 1) / ((div1 + 1) * (1 << div2));
+ if (tclk > rate)
+ continue;
+ /* error will always be +ve */
+ rate_err = rate - tclk;
+ if (rate_err == 0) {
+ *filter = wm8750_get_filter(parent_rate, div1);
+ *multiplier = mul;
+ *divisor1 = div1;
+ *divisor2 = div2;
+ return;
+ }
+
+ if (rate_err < best_err) {
+ best_err = rate_err;
+ best_mul = mul;
+ best_div1 = div1;
+ best_div2 = div2;
+ }
+ }
+
+ /* if we got here, it wasn't an exact match */
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate,
+ rate - best_err);
+
+ *filter = wm8750_get_filter(parent_rate, best_div1);
+ *multiplier = best_mul;
+ *divisor1 = best_div1;
+ *divisor2 = best_div2;
+}
+
+static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 filter, mul, div1, div2;
+ u32 pll_val;
+ unsigned long flags = 0;
+
+ /* sanity check */
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ vt8500_find_pll_bits(rate, parent_rate, &mul, &div1);
+ pll_val = VT8500_BITS_TO_VAL(mul, div1);
+ break;
+ case PLL_TYPE_WM8650:
+ wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2);
+ pll_val = WM8650_BITS_TO_VAL(mul, div1, div2);
+ break;
+ case PLL_TYPE_WM8750:
+ wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2);
+ pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2);
+ break;
+ default:
+ pr_err("%s: invalid pll type\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ vt8500_pmc_wait_busy();
+ writel(pll_val, pll->reg);
+ vt8500_pmc_wait_busy();
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 filter, mul, div1, div2;
+ long round_rate;
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ vt8500_find_pll_bits(rate, *prate, &mul, &div1);
+ round_rate = VT8500_BITS_TO_FREQ(*prate, mul, div1);
+ break;
+ case PLL_TYPE_WM8650:
+ wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2);
+ round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2);
+ break;
+ case PLL_TYPE_WM8750:
+ wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
+ round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
+ break;
+ default:
+ round_rate = 0;
+ }
+
+ return round_rate;
+}
+
+static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 pll_val = readl(pll->reg);
+ unsigned long pll_freq;
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ pll_freq = parent_rate * VT8500_PLL_MUL(pll_val);
+ pll_freq /= VT8500_PLL_DIV(pll_val);
+ break;
+ case PLL_TYPE_WM8650:
+ pll_freq = parent_rate * WM8650_PLL_MUL(pll_val);
+ pll_freq /= WM8650_PLL_DIV(pll_val);
+ break;
+ case PLL_TYPE_WM8750:
+ pll_freq = parent_rate * WM8750_PLL_MUL(pll_val);
+ pll_freq /= WM8750_PLL_DIV(pll_val);
+ break;
+ default:
+ pll_freq = 0;
+ }
+
+ return pll_freq;
+}
+
+const struct clk_ops vtwm_pll_ops = {
+ .round_rate = vtwm_pll_round_rate,
+ .set_rate = vtwm_pll_set_rate,
+ .recalc_rate = vtwm_pll_recalc_rate,
+};
+
+static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type)
+{
+ u32 reg;
+ struct clk *clk;
+ struct clk_pll *pll_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+
+ rc = of_property_read_u32(node, "reg", &reg);
+ if (WARN_ON(rc))
+ return;
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (WARN_ON(!pll_clk))
+ return;
+
+ pll_clk->reg = pmc_base + reg;
+ pll_clk->lock = &_lock;
+ pll_clk->type = pll_type;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &vtwm_pll_ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &pll_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(pll_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+}
+
+
+/* Wrappers for initialization functions */
+
+static void __init vt8500_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_VT8500);
+}
+CLK_OF_DECLARE(vt8500_pll, "via,vt8500-pll-clock", vt8500_pll_init);
+
+static void __init wm8650_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_WM8650);
+}
+CLK_OF_DECLARE(wm8650_pll, "wm,wm8650-pll-clock", wm8650_pll_init);
+
+static void __init wm8750_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_WM8750);
+}
+CLK_OF_DECLARE(wm8750_pll, "wm,wm8750-pll-clock", wm8750_pll_init);
+
+void __init vtwm_clk_init(void __iomem *base)
+{
+ if (!base)
+ return;
+
+ pmc_base = base;
+
+ of_clk_init(NULL);
+}
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
new file mode 100644
index 000000000..917a3ab48
--- /dev/null
+++ b/drivers/clk/clk-wm831x.c
@@ -0,0 +1,414 @@
+/*
+ * WM831x clock control
+ *
+ * Copyright 2011-2 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm831x/core.h>
+
+struct wm831x_clk {
+ struct wm831x *wm831x;
+ struct clk_hw xtal_hw;
+ struct clk_hw fll_hw;
+ struct clk_hw clkout_hw;
+ struct clk *xtal;
+ struct clk *fll;
+ struct clk *clkout;
+ bool xtal_ena;
+};
+
+static int wm831x_xtal_is_enabled(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ xtal_hw);
+
+ return clkdata->xtal_ena;
+}
+
+static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ xtal_hw);
+
+ if (clkdata->xtal_ena)
+ return 32768;
+ else
+ return 0;
+}
+
+static const struct clk_ops wm831x_xtal_ops = {
+ .is_enabled = wm831x_xtal_is_enabled,
+ .recalc_rate = wm831x_xtal_recalc_rate,
+};
+
+static struct clk_init_data wm831x_xtal_init = {
+ .name = "xtal",
+ .ops = &wm831x_xtal_ops,
+ .flags = CLK_IS_ROOT,
+};
+
+static const unsigned long wm831x_fll_auto_rates[] = {
+ 2048000,
+ 11289600,
+ 12000000,
+ 12288000,
+ 19200000,
+ 22579600,
+ 24000000,
+ 24576000,
+};
+
+static int wm831x_fll_is_enabled(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n",
+ ret);
+ return true;
+ }
+
+ return (ret & WM831X_FLL_ENA) != 0;
+}
+
+static int wm831x_fll_prepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2,
+ WM831X_FLL_ENA, WM831X_FLL_ENA);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret);
+
+ usleep_range(2000, 2000);
+
+ return ret;
+}
+
+static void wm831x_fll_unprepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret);
+}
+
+static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
+ ret);
+ return 0;
+ }
+
+ if (ret & WM831X_FLL_AUTO)
+ return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK];
+
+ dev_err(wm831x->dev, "FLL only supported in AUTO mode\n");
+
+ return 0;
+}
+
+static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *unused)
+{
+ int best = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
+ if (abs(wm831x_fll_auto_rates[i] - rate) <
+ abs(wm831x_fll_auto_rates[best] - rate))
+ best = i;
+
+ return wm831x_fll_auto_rates[best];
+}
+
+static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
+ if (wm831x_fll_auto_rates[i] == rate)
+ break;
+ if (i == ARRAY_SIZE(wm831x_fll_auto_rates))
+ return -EINVAL;
+
+ if (wm831x_fll_is_enabled(hw))
+ return -EPERM;
+
+ return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2,
+ WM831X_FLL_AUTO_FREQ_MASK, i);
+}
+
+static const char *wm831x_fll_parents[] = {
+ "xtal",
+ "clkin",
+};
+
+static u8 wm831x_fll_get_parent(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ /* AUTO mode is always clocked from the crystal */
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
+ ret);
+ return 0;
+ }
+
+ if (ret & WM831X_FLL_AUTO)
+ return 0;
+
+ ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n",
+ ret);
+ return 0;
+ }
+
+ switch (ret & WM831X_FLL_CLK_SRC_MASK) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ default:
+ dev_err(wm831x->dev, "Unsupported FLL clock source %d\n",
+ ret & WM831X_FLL_CLK_SRC_MASK);
+ return 0;
+ }
+}
+
+static const struct clk_ops wm831x_fll_ops = {
+ .is_enabled = wm831x_fll_is_enabled,
+ .prepare = wm831x_fll_prepare,
+ .unprepare = wm831x_fll_unprepare,
+ .round_rate = wm831x_fll_round_rate,
+ .recalc_rate = wm831x_fll_recalc_rate,
+ .set_rate = wm831x_fll_set_rate,
+ .get_parent = wm831x_fll_get_parent,
+};
+
+static struct clk_init_data wm831x_fll_init = {
+ .name = "fll",
+ .ops = &wm831x_fll_ops,
+ .parent_names = wm831x_fll_parents,
+ .num_parents = ARRAY_SIZE(wm831x_fll_parents),
+ .flags = CLK_SET_RATE_GATE,
+};
+
+static int wm831x_clkout_is_enabled(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
+ ret);
+ return true;
+ }
+
+ return (ret & WM831X_CLKOUT_ENA) != 0;
+}
+
+static int wm831x_clkout_prepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret != 0) {
+ dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
+ WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret);
+
+ wm831x_reg_lock(wm831x);
+
+ return ret;
+}
+
+static void wm831x_clkout_unprepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret != 0) {
+ dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
+ return;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
+ WM831X_CLKOUT_ENA, 0);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret);
+
+ wm831x_reg_lock(wm831x);
+}
+
+static const char *wm831x_clkout_parents[] = {
+ "xtal",
+ "fll",
+};
+
+static u8 wm831x_clkout_get_parent(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
+ ret);
+ return 0;
+ }
+
+ if (ret & WM831X_CLKOUT_SRC)
+ return 0;
+ else
+ return 1;
+}
+
+static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+
+ return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
+ WM831X_CLKOUT_SRC,
+ parent << WM831X_CLKOUT_SRC_SHIFT);
+}
+
+static const struct clk_ops wm831x_clkout_ops = {
+ .is_enabled = wm831x_clkout_is_enabled,
+ .prepare = wm831x_clkout_prepare,
+ .unprepare = wm831x_clkout_unprepare,
+ .get_parent = wm831x_clkout_get_parent,
+ .set_parent = wm831x_clkout_set_parent,
+};
+
+static struct clk_init_data wm831x_clkout_init = {
+ .name = "clkout",
+ .ops = &wm831x_clkout_ops,
+ .parent_names = wm831x_clkout_parents,
+ .num_parents = ARRAY_SIZE(wm831x_clkout_parents),
+ .flags = CLK_SET_RATE_PARENT,
+};
+
+static int wm831x_clk_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_clk *clkdata;
+ int ret;
+
+ clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
+ if (!clkdata)
+ return -ENOMEM;
+
+ clkdata->wm831x = wm831x;
+
+ /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
+ ret);
+ return ret;
+ }
+ clkdata->xtal_ena = ret & WM831X_XTAL_ENA;
+
+ clkdata->xtal_hw.init = &wm831x_xtal_init;
+ clkdata->xtal = devm_clk_register(&pdev->dev, &clkdata->xtal_hw);
+ if (IS_ERR(clkdata->xtal))
+ return PTR_ERR(clkdata->xtal);
+
+ clkdata->fll_hw.init = &wm831x_fll_init;
+ clkdata->fll = devm_clk_register(&pdev->dev, &clkdata->fll_hw);
+ if (IS_ERR(clkdata->fll))
+ return PTR_ERR(clkdata->fll);
+
+ clkdata->clkout_hw.init = &wm831x_clkout_init;
+ clkdata->clkout = devm_clk_register(&pdev->dev, &clkdata->clkout_hw);
+ if (IS_ERR(clkdata->clkout))
+ return PTR_ERR(clkdata->clkout);
+
+ dev_set_drvdata(&pdev->dev, clkdata);
+
+ return 0;
+}
+
+static int wm831x_clk_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver wm831x_clk_driver = {
+ .probe = wm831x_clk_probe,
+ .remove = wm831x_clk_remove,
+ .driver = {
+ .name = "wm831x-clk",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(wm831x_clk_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x clock driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-clk");
diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c
new file mode 100644
index 000000000..32062977f
--- /dev/null
+++ b/drivers/clk/clk-zynq.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2012 National Instruments
+ *
+ * Josh Cartwright <josh.cartwright@ni.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/zynq.h>
+
+static void __iomem *slcr_base;
+
+struct zynq_pll_clk {
+ struct clk_hw hw;
+ void __iomem *pll_ctrl;
+ void __iomem *pll_cfg;
+};
+
+#define to_zynq_pll_clk(hw) container_of(hw, struct zynq_pll_clk, hw)
+
+#define CTRL_PLL_FDIV(x) ((x) >> 12)
+
+static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct zynq_pll_clk *pll = to_zynq_pll_clk(hw);
+ return parent_rate * CTRL_PLL_FDIV(ioread32(pll->pll_ctrl));
+}
+
+static const struct clk_ops zynq_pll_clk_ops = {
+ .recalc_rate = zynq_pll_recalc_rate,
+};
+
+static void __init zynq_pll_clk_setup(struct device_node *np)
+{
+ struct clk_init_data init;
+ struct zynq_pll_clk *pll;
+ const char *parent_name;
+ struct clk *clk;
+ u32 regs[2];
+ int ret;
+
+ ret = of_property_read_u32_array(np, "reg", regs, ARRAY_SIZE(regs));
+ if (WARN_ON(ret))
+ return;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (WARN_ON(!pll))
+ return;
+
+ pll->pll_ctrl = slcr_base + regs[0];
+ pll->pll_cfg = slcr_base + regs[1];
+
+ of_property_read_string(np, "clock-output-names", &init.name);
+
+ init.ops = &zynq_pll_clk_ops;
+ parent_name = of_clk_get_parent_name(np, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (WARN_ON(ret))
+ return;
+}
+CLK_OF_DECLARE(zynq_pll, "xlnx,zynq-pll", zynq_pll_clk_setup);
+
+struct zynq_periph_clk {
+ struct clk_hw hw;
+ struct clk_onecell_data onecell_data;
+ struct clk *gates[2];
+ void __iomem *clk_ctrl;
+ spinlock_t clkact_lock;
+};
+
+#define to_zynq_periph_clk(hw) container_of(hw, struct zynq_periph_clk, hw)
+
+static const u8 periph_clk_parent_map[] = {
+ 0, 0, 1, 2
+};
+#define PERIPH_CLK_CTRL_SRC(x) (periph_clk_parent_map[((x) & 0x30) >> 4])
+#define PERIPH_CLK_CTRL_DIV(x) (((x) & 0x3F00) >> 8)
+
+static unsigned long zynq_periph_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct zynq_periph_clk *periph = to_zynq_periph_clk(hw);
+ return parent_rate / PERIPH_CLK_CTRL_DIV(ioread32(periph->clk_ctrl));
+}
+
+static u8 zynq_periph_get_parent(struct clk_hw *hw)
+{
+ struct zynq_periph_clk *periph = to_zynq_periph_clk(hw);
+ return PERIPH_CLK_CTRL_SRC(ioread32(periph->clk_ctrl));
+}
+
+static const struct clk_ops zynq_periph_clk_ops = {
+ .recalc_rate = zynq_periph_recalc_rate,
+ .get_parent = zynq_periph_get_parent,
+};
+
+static void __init zynq_periph_clk_setup(struct device_node *np)
+{
+ struct zynq_periph_clk *periph;
+ const char *parent_names[3];
+ struct clk_init_data init;
+ int clk_num = 0, err;
+ const char *name;
+ struct clk *clk;
+ u32 reg;
+ int i;
+
+ err = of_property_read_u32(np, "reg", &reg);
+ if (WARN_ON(err))
+ return;
+
+ periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+ if (WARN_ON(!periph))
+ return;
+
+ periph->clk_ctrl = slcr_base + reg;
+ spin_lock_init(&periph->clkact_lock);
+
+ init.name = np->name;
+ init.ops = &zynq_periph_clk_ops;
+ for (i = 0; i < ARRAY_SIZE(parent_names); i++)
+ parent_names[i] = of_clk_get_parent_name(np, i);
+ init.parent_names = parent_names;
+ init.num_parents = ARRAY_SIZE(parent_names);
+
+ periph->hw.init = &init;
+
+ clk = clk_register(NULL, &periph->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return;
+
+ err = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (WARN_ON(err))
+ return;
+
+ err = of_property_read_string_index(np, "clock-output-names", 0,
+ &name);
+ if (WARN_ON(err))
+ return;
+
+ periph->gates[0] = clk_register_gate(NULL, name, np->name, 0,
+ periph->clk_ctrl, 0, 0,
+ &periph->clkact_lock);
+ if (WARN_ON(IS_ERR(periph->gates[0])))
+ return;
+ clk_num++;
+
+ /* some periph clks have 2 downstream gates */
+ err = of_property_read_string_index(np, "clock-output-names", 1,
+ &name);
+ if (err != -ENODATA) {
+ periph->gates[1] = clk_register_gate(NULL, name, np->name, 0,
+ periph->clk_ctrl, 1, 0,
+ &periph->clkact_lock);
+ if (WARN_ON(IS_ERR(periph->gates[1])))
+ return;
+ clk_num++;
+ }
+
+ periph->onecell_data.clks = periph->gates;
+ periph->onecell_data.clk_num = clk_num;
+
+ err = of_clk_add_provider(np, of_clk_src_onecell_get,
+ &periph->onecell_data);
+ if (WARN_ON(err))
+ return;
+}
+CLK_OF_DECLARE(zynq_periph, "xlnx,zynq-periph-clock", zynq_periph_clk_setup);
+
+/* CPU Clock domain is modelled as a mux with 4 children subclks, whose
+ * derivative rates depend on CLK_621_TRUE
+ */
+
+struct zynq_cpu_clk {
+ struct clk_hw hw;
+ struct clk_onecell_data onecell_data;
+ struct clk *subclks[4];
+ void __iomem *clk_ctrl;
+ spinlock_t clkact_lock;
+};
+
+#define to_zynq_cpu_clk(hw) container_of(hw, struct zynq_cpu_clk, hw)
+
+static const u8 zynq_cpu_clk_parent_map[] = {
+ 1, 1, 2, 0
+};
+#define CPU_CLK_SRCSEL(x) (zynq_cpu_clk_parent_map[(((x) & 0x30) >> 4)])
+#define CPU_CLK_CTRL_DIV(x) (((x) & 0x3F00) >> 8)
+
+static u8 zynq_cpu_clk_get_parent(struct clk_hw *hw)
+{
+ struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw);
+ return CPU_CLK_SRCSEL(ioread32(cpuclk->clk_ctrl));
+}
+
+static unsigned long zynq_cpu_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw);
+ return parent_rate / CPU_CLK_CTRL_DIV(ioread32(cpuclk->clk_ctrl));
+}
+
+static const struct clk_ops zynq_cpu_clk_ops = {
+ .get_parent = zynq_cpu_clk_get_parent,
+ .recalc_rate = zynq_cpu_clk_recalc_rate,
+};
+
+struct zynq_cpu_subclk {
+ struct clk_hw hw;
+ void __iomem *clk_621;
+ enum {
+ CPU_SUBCLK_6X4X,
+ CPU_SUBCLK_3X2X,
+ CPU_SUBCLK_2X,
+ CPU_SUBCLK_1X,
+ } which;
+};
+
+#define CLK_621_TRUE(x) ((x) & 1)
+
+#define to_zynq_cpu_subclk(hw) container_of(hw, struct zynq_cpu_subclk, hw);
+
+static unsigned long zynq_cpu_subclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long uninitialized_var(rate);
+ struct zynq_cpu_subclk *subclk;
+ bool is_621;
+
+ subclk = to_zynq_cpu_subclk(hw)
+ is_621 = CLK_621_TRUE(ioread32(subclk->clk_621));
+
+ switch (subclk->which) {
+ case CPU_SUBCLK_6X4X:
+ rate = parent_rate;
+ break;
+ case CPU_SUBCLK_3X2X:
+ rate = parent_rate / 2;
+ break;
+ case CPU_SUBCLK_2X:
+ rate = parent_rate / (is_621 ? 3 : 2);
+ break;
+ case CPU_SUBCLK_1X:
+ rate = parent_rate / (is_621 ? 6 : 4);
+ break;
+ };
+
+ return rate;
+}
+
+static const struct clk_ops zynq_cpu_subclk_ops = {
+ .recalc_rate = zynq_cpu_subclk_recalc_rate,
+};
+
+static struct clk *zynq_cpu_subclk_setup(struct device_node *np, u8 which,
+ void __iomem *clk_621)
+{
+ struct zynq_cpu_subclk *subclk;
+ struct clk_init_data init;
+ struct clk *clk;
+ int err;
+
+ err = of_property_read_string_index(np, "clock-output-names",
+ which, &init.name);
+ if (WARN_ON(err))
+ goto err_read_output_name;
+
+ subclk = kzalloc(sizeof(*subclk), GFP_KERNEL);
+ if (!subclk)
+ goto err_subclk_alloc;
+
+ subclk->clk_621 = clk_621;
+ subclk->which = which;
+
+ init.ops = &zynq_cpu_subclk_ops;
+ init.parent_names = &np->name;
+ init.num_parents = 1;
+
+ subclk->hw.init = &init;
+
+ clk = clk_register(NULL, &subclk->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ goto err_clk_register;
+
+ return clk;
+
+err_clk_register:
+ kfree(subclk);
+err_subclk_alloc:
+err_read_output_name:
+ return ERR_PTR(-EINVAL);
+}
+
+static void __init zynq_cpu_clk_setup(struct device_node *np)
+{
+ struct zynq_cpu_clk *cpuclk;
+ const char *parent_names[3];
+ struct clk_init_data init;
+ void __iomem *clk_621;
+ struct clk *clk;
+ u32 reg[2];
+ int err;
+ int i;
+
+ err = of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg));
+ if (WARN_ON(err))
+ return;
+
+ cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
+ if (WARN_ON(!cpuclk))
+ return;
+
+ cpuclk->clk_ctrl = slcr_base + reg[0];
+ clk_621 = slcr_base + reg[1];
+ spin_lock_init(&cpuclk->clkact_lock);
+
+ init.name = np->name;
+ init.ops = &zynq_cpu_clk_ops;
+ for (i = 0; i < ARRAY_SIZE(parent_names); i++)
+ parent_names[i] = of_clk_get_parent_name(np, i);
+ init.parent_names = parent_names;
+ init.num_parents = ARRAY_SIZE(parent_names);
+
+ cpuclk->hw.init = &init;
+
+ clk = clk_register(NULL, &cpuclk->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return;
+
+ err = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (WARN_ON(err))
+ return;
+
+ for (i = 0; i < 4; i++) {
+ cpuclk->subclks[i] = zynq_cpu_subclk_setup(np, i, clk_621);
+ if (WARN_ON(IS_ERR(cpuclk->subclks[i])))
+ return;
+ }
+
+ cpuclk->onecell_data.clks = cpuclk->subclks;
+ cpuclk->onecell_data.clk_num = i;
+
+ err = of_clk_add_provider(np, of_clk_src_onecell_get,
+ &cpuclk->onecell_data);
+ if (WARN_ON(err))
+ return;
+}
+CLK_OF_DECLARE(zynq_cpu, "xlnx,zynq-cpu-clock", zynq_cpu_clk_setup);
+
+void __init xilinx_zynq_clocks_init(void __iomem *slcr)
+{
+ slcr_base = slcr;
+ of_clk_init(NULL);
+}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
new file mode 100644
index 000000000..1144e8c75
--- /dev/null
+++ b/drivers/clk/clk.c
@@ -0,0 +1,2131 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Standard functionality for the common clock API. See Documentation/clk.txt
+ */
+
+#include <linux/clk-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+static DEFINE_SPINLOCK(enable_lock);
+static DEFINE_MUTEX(prepare_lock);
+
+static struct task_struct *prepare_owner;
+static struct task_struct *enable_owner;
+
+static int prepare_refcnt;
+static int enable_refcnt;
+
+static HLIST_HEAD(clk_root_list);
+static HLIST_HEAD(clk_orphan_list);
+static LIST_HEAD(clk_notifier_list);
+
+/*** locking ***/
+static void clk_prepare_lock(void)
+{
+ if (!mutex_trylock(&prepare_lock)) {
+ if (prepare_owner == current) {
+ prepare_refcnt++;
+ return;
+ }
+ mutex_lock(&prepare_lock);
+ }
+ WARN_ON_ONCE(prepare_owner != NULL);
+ WARN_ON_ONCE(prepare_refcnt != 0);
+ prepare_owner = current;
+ prepare_refcnt = 1;
+}
+
+static void clk_prepare_unlock(void)
+{
+ WARN_ON_ONCE(prepare_owner != current);
+ WARN_ON_ONCE(prepare_refcnt == 0);
+
+ if (--prepare_refcnt)
+ return;
+ prepare_owner = NULL;
+ mutex_unlock(&prepare_lock);
+}
+
+static unsigned long clk_enable_lock(void)
+{
+ unsigned long flags;
+
+ if (!spin_trylock_irqsave(&enable_lock, flags)) {
+ if (enable_owner == current) {
+ enable_refcnt++;
+ return flags;
+ }
+ spin_lock_irqsave(&enable_lock, flags);
+ }
+ WARN_ON_ONCE(enable_owner != NULL);
+ WARN_ON_ONCE(enable_refcnt != 0);
+ enable_owner = current;
+ enable_refcnt = 1;
+ return flags;
+}
+
+static void clk_enable_unlock(unsigned long flags)
+{
+ WARN_ON_ONCE(enable_owner != current);
+ WARN_ON_ONCE(enable_refcnt == 0);
+
+ if (--enable_refcnt)
+ return;
+ enable_owner = NULL;
+ spin_unlock_irqrestore(&enable_lock, flags);
+}
+
+/*** debugfs support ***/
+
+#ifdef CONFIG_COMMON_CLK_DEBUG
+#include <linux/debugfs.h>
+
+static struct dentry *rootdir;
+static struct dentry *orphandir;
+static int inited = 0;
+
+static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
+{
+ if (!c)
+ return;
+
+ seq_printf(s, "%*s%-*s %-11d %-12d %-10lu",
+ level * 3 + 1, "",
+ 30 - level * 3, c->name,
+ c->enable_count, c->prepare_count, c->rate);
+ seq_printf(s, "\n");
+}
+
+static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
+ int level)
+{
+ struct clk *child;
+
+ if (!c)
+ return;
+
+ clk_summary_show_one(s, c, level);
+
+ hlist_for_each_entry(child, &c->children, child_node)
+ clk_summary_show_subtree(s, child, level + 1);
+}
+
+static int clk_summary_show(struct seq_file *s, void *data)
+{
+ struct clk *c;
+
+ seq_printf(s, " clock enable_cnt prepare_cnt rate\n");
+ seq_printf(s, "---------------------------------------------------------------------\n");
+
+ clk_prepare_lock();
+
+ hlist_for_each_entry(c, &clk_root_list, child_node)
+ clk_summary_show_subtree(s, c, 0);
+
+ hlist_for_each_entry(c, &clk_orphan_list, child_node)
+ clk_summary_show_subtree(s, c, 0);
+
+ clk_prepare_unlock();
+
+ return 0;
+}
+
+
+static int clk_summary_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_summary_show, inode->i_private);
+}
+
+static const struct file_operations clk_summary_fops = {
+ .open = clk_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
+{
+ if (!c)
+ return;
+
+ seq_printf(s, "\"%s\": { ", c->name);
+ seq_printf(s, "\"enable_count\": %d,", c->enable_count);
+ seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
+ seq_printf(s, "\"rate\": %lu", c->rate);
+}
+
+static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
+{
+ struct clk *child;
+
+ if (!c)
+ return;
+
+ clk_dump_one(s, c, level);
+
+ hlist_for_each_entry(child, &c->children, child_node) {
+ seq_printf(s, ",");
+ clk_dump_subtree(s, child, level + 1);
+ }
+
+ seq_printf(s, "}");
+}
+
+static int clk_dump(struct seq_file *s, void *data)
+{
+ struct clk *c;
+ bool first_node = true;
+
+ seq_printf(s, "{");
+
+ clk_prepare_lock();
+
+ hlist_for_each_entry(c, &clk_root_list, child_node) {
+ if (!first_node)
+ seq_printf(s, ",");
+ first_node = false;
+ clk_dump_subtree(s, c, 0);
+ }
+
+ hlist_for_each_entry(c, &clk_orphan_list, child_node) {
+ seq_printf(s, ",");
+ clk_dump_subtree(s, c, 0);
+ }
+
+ clk_prepare_unlock();
+
+ seq_printf(s, "}");
+ return 0;
+}
+
+
+static int clk_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_dump, inode->i_private);
+}
+
+static const struct file_operations clk_dump_fops = {
+ .open = clk_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* caller must hold prepare_lock */
+static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
+{
+ struct dentry *d;
+ int ret = -ENOMEM;
+
+ if (!clk || !pdentry) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ d = debugfs_create_dir(clk->name, pdentry);
+ if (!d)
+ goto out;
+
+ clk->dentry = d;
+
+ d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry,
+ (u32 *)&clk->rate);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
+ (u32 *)&clk->flags);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->prepare_count);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->enable_count);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->notifier_count);
+ if (!d)
+ goto err_out;
+
+ ret = 0;
+ goto out;
+
+err_out:
+ debugfs_remove(clk->dentry);
+out:
+ return ret;
+}
+
+/* caller must hold prepare_lock */
+static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry)
+{
+ struct clk *child;
+ int ret = -EINVAL;;
+
+ if (!clk || !pdentry)
+ goto out;
+
+ ret = clk_debug_create_one(clk, pdentry);
+
+ if (ret)
+ goto out;
+
+ hlist_for_each_entry(child, &clk->children, child_node)
+ clk_debug_create_subtree(child, clk->dentry);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * clk_debug_register - add a clk node to the debugfs clk tree
+ * @clk: the clk being added to the debugfs clk tree
+ *
+ * Dynamically adds a clk to the debugfs clk tree if debugfs has been
+ * initialized. Otherwise it bails out early since the debugfs clk tree
+ * will be created lazily by clk_debug_init as part of a late_initcall.
+ *
+ * Caller must hold prepare_lock. Only clk_init calls this function (so
+ * far) so this is taken care.
+ */
+static int clk_debug_register(struct clk *clk)
+{
+ struct clk *parent;
+ struct dentry *pdentry;
+ int ret = 0;
+
+ if (!inited)
+ goto out;
+
+ parent = clk->parent;
+
+ /*
+ * Check to see if a clk is a root clk. Also check that it is
+ * safe to add this clk to debugfs
+ */
+ if (!parent)
+ if (clk->flags & CLK_IS_ROOT)
+ pdentry = rootdir;
+ else
+ pdentry = orphandir;
+ else
+ if (parent->dentry)
+ pdentry = parent->dentry;
+ else
+ goto out;
+
+ ret = clk_debug_create_subtree(clk, pdentry);
+
+out:
+ return ret;
+}
+
+/**
+ * clk_debug_reparent - reparent clk node in the debugfs clk tree
+ * @clk: the clk being reparented
+ * @new_parent: the new clk parent, may be NULL
+ *
+ * Rename clk entry in the debugfs clk tree if debugfs has been
+ * initialized. Otherwise it bails out early since the debugfs clk tree
+ * will be created lazily by clk_debug_init as part of a late_initcall.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
+{
+ struct dentry *d;
+ struct dentry *new_parent_d;
+
+ if (!inited)
+ return;
+
+ if (new_parent)
+ new_parent_d = new_parent->dentry;
+ else
+ new_parent_d = orphandir;
+
+ d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
+ new_parent_d, clk->name);
+ if (d)
+ clk->dentry = d;
+ else
+ pr_debug("%s: failed to rename debugfs entry for %s\n",
+ __func__, clk->name);
+}
+
+/**
+ * clk_debug_init - lazily create the debugfs clk tree visualization
+ *
+ * clks are often initialized very early during boot before memory can
+ * be dynamically allocated and well before debugfs is setup.
+ * clk_debug_init walks the clk tree hierarchy while holding
+ * prepare_lock and creates the topology as part of a late_initcall,
+ * thus insuring that clks initialized very early will still be
+ * represented in the debugfs clk tree. This function should only be
+ * called once at boot-time, and all other clks added dynamically will
+ * be done so with clk_debug_register.
+ */
+static int __init clk_debug_init(void)
+{
+ struct clk *clk;
+ struct dentry *d;
+
+ rootdir = debugfs_create_dir("clk", NULL);
+
+ if (!rootdir)
+ return -ENOMEM;
+
+ d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, NULL,
+ &clk_summary_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, NULL,
+ &clk_dump_fops);
+ if (!d)
+ return -ENOMEM;
+
+ orphandir = debugfs_create_dir("orphans", rootdir);
+
+ if (!orphandir)
+ return -ENOMEM;
+
+ clk_prepare_lock();
+
+ hlist_for_each_entry(clk, &clk_root_list, child_node)
+ clk_debug_create_subtree(clk, rootdir);
+
+ hlist_for_each_entry(clk, &clk_orphan_list, child_node)
+ clk_debug_create_subtree(clk, orphandir);
+
+ inited = 1;
+
+ clk_prepare_unlock();
+
+ return 0;
+}
+late_initcall(clk_debug_init);
+#else
+static inline int clk_debug_register(struct clk *clk) { return 0; }
+static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
+{
+}
+#endif
+
+/* caller must hold prepare_lock */
+static void clk_unprepare_unused_subtree(struct clk *clk)
+{
+ struct clk *child;
+
+ if (!clk)
+ return;
+
+ hlist_for_each_entry(child, &clk->children, child_node)
+ clk_unprepare_unused_subtree(child);
+
+ if (clk->prepare_count)
+ return;
+
+ if (clk->flags & CLK_IGNORE_UNUSED)
+ return;
+
+ if (__clk_is_prepared(clk)) {
+ if (clk->ops->unprepare_unused)
+ clk->ops->unprepare_unused(clk->hw);
+ else if (clk->ops->unprepare)
+ clk->ops->unprepare(clk->hw);
+ }
+}
+EXPORT_SYMBOL_GPL(__clk_get_flags);
+
+/* caller must hold prepare_lock */
+static void clk_disable_unused_subtree(struct clk *clk)
+{
+ struct clk *child;
+ unsigned long flags;
+
+ if (!clk)
+ goto out;
+
+ hlist_for_each_entry(child, &clk->children, child_node)
+ clk_disable_unused_subtree(child);
+
+ flags = clk_enable_lock();
+
+ if (clk->enable_count)
+ goto unlock_out;
+
+ if (clk->flags & CLK_IGNORE_UNUSED)
+ goto unlock_out;
+
+ /*
+ * some gate clocks have special needs during the disable-unused
+ * sequence. call .disable_unused if available, otherwise fall
+ * back to .disable
+ */
+ if (__clk_is_enabled(clk)) {
+ if (clk->ops->disable_unused)
+ clk->ops->disable_unused(clk->hw);
+ else if (clk->ops->disable)
+ clk->ops->disable(clk->hw);
+ }
+
+unlock_out:
+ clk_enable_unlock(flags);
+
+out:
+ return;
+}
+
+static bool clk_ignore_unused;
+static int __init clk_ignore_unused_setup(char *__unused)
+{
+ clk_ignore_unused = true;
+ return 1;
+}
+__setup("clk_ignore_unused", clk_ignore_unused_setup);
+
+static int clk_disable_unused(void)
+{
+ struct clk *clk;
+
+ if (clk_ignore_unused) {
+ pr_warn("clk: Not disabling unused clocks\n");
+ return 0;
+ }
+
+ clk_prepare_lock();
+
+ hlist_for_each_entry(clk, &clk_root_list, child_node)
+ clk_disable_unused_subtree(clk);
+
+ hlist_for_each_entry(clk, &clk_orphan_list, child_node)
+ clk_disable_unused_subtree(clk);
+
+ hlist_for_each_entry(clk, &clk_root_list, child_node)
+ clk_unprepare_unused_subtree(clk);
+
+ hlist_for_each_entry(clk, &clk_orphan_list, child_node)
+ clk_unprepare_unused_subtree(clk);
+
+ clk_prepare_unlock();
+
+ return 0;
+}
+late_initcall(clk_disable_unused);
+
+/*** helper functions ***/
+
+const char *__clk_get_name(struct clk *clk)
+{
+ return !clk ? NULL : clk->name;
+}
+EXPORT_SYMBOL_GPL(__clk_get_name);
+
+struct clk_hw *__clk_get_hw(struct clk *clk)
+{
+ return !clk ? NULL : clk->hw;
+}
+
+u8 __clk_get_num_parents(struct clk *clk)
+{
+ return !clk ? 0 : clk->num_parents;
+}
+
+struct clk *__clk_get_parent(struct clk *clk)
+{
+ return !clk ? NULL : clk->parent;
+}
+
+unsigned int __clk_get_enable_count(struct clk *clk)
+{
+ return !clk ? 0 : clk->enable_count;
+}
+
+unsigned int __clk_get_prepare_count(struct clk *clk)
+{
+ return !clk ? 0 : clk->prepare_count;
+}
+
+unsigned long __clk_get_rate(struct clk *clk)
+{
+ unsigned long ret;
+
+ if (!clk) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = clk->rate;
+
+ if (clk->flags & CLK_IS_ROOT)
+ goto out;
+
+ if (!clk->parent)
+ ret = 0;
+
+out:
+ return ret;
+}
+
+unsigned long __clk_get_flags(struct clk *clk)
+{
+ return !clk ? 0 : clk->flags;
+}
+
+bool __clk_is_prepared(struct clk *clk)
+{
+ int ret;
+
+ if (!clk)
+ return false;
+
+ /*
+ * .is_prepared is optional for clocks that can prepare
+ * fall back to software usage counter if it is missing
+ */
+ if (!clk->ops->is_prepared) {
+ ret = clk->prepare_count ? 1 : 0;
+ goto out;
+ }
+
+ ret = clk->ops->is_prepared(clk->hw);
+out:
+ return !!ret;
+}
+
+bool __clk_is_enabled(struct clk *clk)
+{
+ int ret;
+
+ if (!clk)
+ return false;
+
+ /*
+ * .is_enabled is only mandatory for clocks that gate
+ * fall back to software usage counter if .is_enabled is missing
+ */
+ if (!clk->ops->is_enabled) {
+ ret = clk->enable_count ? 1 : 0;
+ goto out;
+ }
+
+ ret = clk->ops->is_enabled(clk->hw);
+out:
+ return !!ret;
+}
+
+static struct clk *__clk_lookup_subtree(const char *name, struct clk *clk)
+{
+ struct clk *child;
+ struct clk *ret;
+
+ if (!strcmp(clk->name, name))
+ return clk;
+
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ ret = __clk_lookup_subtree(name, child);
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+struct clk *__clk_lookup(const char *name)
+{
+ struct clk *root_clk;
+ struct clk *ret;
+
+ if (!name)
+ return NULL;
+
+ /* search the 'proper' clk tree first */
+ hlist_for_each_entry(root_clk, &clk_root_list, child_node) {
+ ret = __clk_lookup_subtree(name, root_clk);
+ if (ret)
+ return ret;
+ }
+
+ /* if not found, then search the orphan tree */
+ hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) {
+ ret = __clk_lookup_subtree(name, root_clk);
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*** clk api ***/
+
+void __clk_unprepare(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return;
+
+ if (--clk->prepare_count > 0)
+ return;
+
+ WARN_ON(clk->enable_count > 0);
+
+ if (clk->ops->unprepare)
+ clk->ops->unprepare(clk->hw);
+
+ __clk_unprepare(clk->parent);
+}
+
+/**
+ * clk_unprepare - undo preparation of a clock source
+ * @clk: the clk being unprepare
+ *
+ * clk_unprepare may sleep, which differentiates it from clk_disable. In a
+ * simple case, clk_unprepare can be used instead of clk_disable to gate a clk
+ * if the operation may sleep. One example is a clk which is accessed over
+ * I2c. In the complex case a clk gate operation may require a fast and a slow
+ * part. It is this reason that clk_unprepare and clk_disable are not mutually
+ * exclusive. In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_unprepare(struct clk *clk)
+{
+ clk_prepare_lock();
+ __clk_unprepare(clk);
+ clk_prepare_unlock();
+}
+EXPORT_SYMBOL_GPL(clk_unprepare);
+
+int __clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (clk->prepare_count == 0) {
+ ret = __clk_prepare(clk->parent);
+ if (ret)
+ return ret;
+
+ if (clk->ops->prepare) {
+ ret = clk->ops->prepare(clk->hw);
+ if (ret) {
+ __clk_unprepare(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->prepare_count++;
+
+ return 0;
+}
+
+/**
+ * clk_prepare - prepare a clock source
+ * @clk: the clk being prepared
+ *
+ * clk_prepare may sleep, which differentiates it from clk_enable. In a simple
+ * case, clk_prepare can be used instead of clk_enable to ungate a clk if the
+ * operation may sleep. One example is a clk which is accessed over I2c. In
+ * the complex case a clk ungate operation may require a fast and a slow part.
+ * It is this reason that clk_prepare and clk_enable are not mutually
+ * exclusive. In fact clk_prepare must be called before clk_enable.
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_prepare(struct clk *clk)
+{
+ int ret;
+
+ clk_prepare_lock();
+ ret = __clk_prepare(clk);
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_prepare);
+
+static void __clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(IS_ERR(clk)))
+ return;
+
+ if (WARN_ON(clk->enable_count == 0))
+ return;
+
+ if (--clk->enable_count > 0)
+ return;
+
+ if (clk->ops->disable)
+ clk->ops->disable(clk->hw);
+
+ __clk_disable(clk->parent);
+}
+
+/**
+ * clk_disable - gate a clock
+ * @clk: the clk being gated
+ *
+ * clk_disable must not sleep, which differentiates it from clk_unprepare. In
+ * a simple case, clk_disable can be used instead of clk_unprepare to gate a
+ * clk if the operation is fast and will never sleep. One example is a
+ * SoC-internal clk which is controlled via simple register writes. In the
+ * complex case a clk gate operation may require a fast and a slow part. It is
+ * this reason that clk_unprepare and clk_disable are not mutually exclusive.
+ * In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ flags = clk_enable_lock();
+ __clk_disable(clk);
+ clk_enable_unlock(flags);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+static int __clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return -ESHUTDOWN;
+
+ if (clk->enable_count == 0) {
+ ret = __clk_enable(clk->parent);
+
+ if (ret)
+ return ret;
+
+ if (clk->ops->enable) {
+ ret = clk->ops->enable(clk->hw);
+ if (ret) {
+ __clk_disable(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->enable_count++;
+ return 0;
+}
+
+/**
+ * clk_enable - ungate a clock
+ * @clk: the clk being ungated
+ *
+ * clk_enable must not sleep, which differentiates it from clk_prepare. In a
+ * simple case, clk_enable can be used instead of clk_prepare to ungate a clk
+ * if the operation will never sleep. One example is a SoC-internal clk which
+ * is controlled via simple register writes. In the complex case a clk ungate
+ * operation may require a fast and a slow part. It is this reason that
+ * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare
+ * must be called before clk_enable. Returns 0 on success, -EERROR
+ * otherwise.
+ */
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ flags = clk_enable_lock();
+ ret = __clk_enable(clk);
+ clk_enable_unlock(flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+/**
+ * __clk_round_rate - round the given rate for a clk
+ * @clk: round the rate of this clock
+ *
+ * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate
+ */
+unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long parent_rate = 0;
+
+ if (!clk)
+ return 0;
+
+ if (!clk->ops->round_rate) {
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ return __clk_round_rate(clk->parent, rate);
+ else
+ return clk->rate;
+ }
+
+ if (clk->parent)
+ parent_rate = clk->parent->rate;
+
+ return clk->ops->round_rate(clk->hw, rate, &parent_rate);
+}
+
+/**
+ * clk_round_rate - round the given rate for a clk
+ * @clk: the clk for which we are rounding a rate
+ * @rate: the rate which is to be rounded
+ *
+ * Takes in a rate as input and rounds it to a rate that the clk can actually
+ * use which is then returned. If clk doesn't support round_rate operation
+ * then the parent rate is returned.
+ */
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long ret;
+
+ clk_prepare_lock();
+ ret = __clk_round_rate(clk, rate);
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+/**
+ * __clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (see include/linux/clk.h)
+ * @old_rate: old clk rate
+ * @new_rate: new clk rate
+ *
+ * Triggers a notifier call chain on the clk rate-change notification
+ * for 'clk'. Passes a pointer to the struct clk and the previous
+ * and current rates to the notifier callback. Intended to be called by
+ * internal clock code only. Returns NOTIFY_DONE from the last driver
+ * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if
+ * a driver returns that.
+ */
+static int __clk_notify(struct clk *clk, unsigned long msg,
+ unsigned long old_rate, unsigned long new_rate)
+{
+ struct clk_notifier *cn;
+ struct clk_notifier_data cnd;
+ int ret = NOTIFY_DONE;
+
+ cnd.clk = clk;
+ cnd.old_rate = old_rate;
+ cnd.new_rate = new_rate;
+
+ list_for_each_entry(cn, &clk_notifier_list, node) {
+ if (cn->clk == clk) {
+ ret = srcu_notifier_call_chain(&cn->notifier_head, msg,
+ &cnd);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * __clk_recalc_rates
+ * @clk: first clk in the subtree
+ * @msg: notification type (see include/linux/clk.h)
+ *
+ * Walks the subtree of clks starting with clk and recalculates rates as it
+ * goes. Note that if a clk does not implement the .recalc_rate callback then
+ * it is assumed that the clock will take on the rate of it's parent.
+ *
+ * clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
+ * if necessary.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
+{
+ unsigned long old_rate;
+ unsigned long parent_rate = 0;
+ struct clk *child;
+
+ old_rate = clk->rate;
+
+ if (clk->parent)
+ parent_rate = clk->parent->rate;
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ clk->rate = parent_rate;
+
+ /*
+ * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE
+ * & ABORT_RATE_CHANGE notifiers
+ */
+ if (clk->notifier_count && msg)
+ __clk_notify(clk, msg, old_rate, clk->rate);
+
+ hlist_for_each_entry(child, &clk->children, child_node)
+ __clk_recalc_rates(child, msg);
+}
+
+/**
+ * clk_get_rate - return the rate of clk
+ * @clk: the clk whose rate is being returned
+ *
+ * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag
+ * is set, which means a recalc_rate will be issued.
+ * If clk is NULL then returns 0.
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+
+ clk_prepare_lock();
+
+ if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
+ __clk_recalc_rates(clk, 0);
+
+ rate = __clk_get_rate(clk);
+ clk_prepare_unlock();
+
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+/**
+ * __clk_speculate_rates
+ * @clk: first clk in the subtree
+ * @parent_rate: the "future" rate of clk's parent
+ *
+ * Walks the subtree of clks starting with clk, speculating rates as it
+ * goes and firing off PRE_RATE_CHANGE notifications as necessary.
+ *
+ * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending
+ * pre-rate change notifications and returns early if no clks in the
+ * subtree have subscribed to the notifications. Note that if a clk does not
+ * implement the .recalc_rate callback then it is assumed that the clock will
+ * take on the rate of it's parent.
+ *
+ * Caller must hold prepare_lock.
+ */
+static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
+{
+ struct clk *child;
+ unsigned long new_rate;
+ int ret = NOTIFY_DONE;
+
+ if (clk->ops->recalc_rate)
+ new_rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ new_rate = parent_rate;
+
+ /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
+ if (clk->notifier_count)
+ ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
+
+ if (ret & NOTIFY_STOP_MASK)
+ goto out;
+
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ ret = __clk_speculate_rates(child, new_rate);
+ if (ret & NOTIFY_STOP_MASK)
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
+{
+ struct clk *child;
+
+ clk->new_rate = new_rate;
+
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ if (child->ops->recalc_rate)
+ child->new_rate = child->ops->recalc_rate(child->hw, new_rate);
+ else
+ child->new_rate = new_rate;
+ clk_calc_subtree(child, child->new_rate);
+ }
+}
+
+/*
+ * calculate the new rates returning the topmost clock that has to be
+ * changed.
+ */
+static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
+{
+ struct clk *top = clk;
+ unsigned long best_parent_rate = 0;
+ unsigned long new_rate;
+
+ /* sanity */
+ if (IS_ERR_OR_NULL(clk))
+ return NULL;
+
+ /* save parent rate, if it exists */
+ if (clk->parent)
+ best_parent_rate = clk->parent->rate;
+
+ /* never propagate up to the parent */
+ if (!(clk->flags & CLK_SET_RATE_PARENT)) {
+ if (!clk->ops->round_rate) {
+ clk->new_rate = clk->rate;
+ return NULL;
+ }
+ new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
+ goto out;
+ }
+
+ /* need clk->parent from here on out */
+ if (!clk->parent) {
+ pr_debug("%s: %s has NULL parent\n", __func__, clk->name);
+ return NULL;
+ }
+
+ if (!clk->ops->round_rate) {
+ top = clk_calc_new_rates(clk->parent, rate);
+ new_rate = clk->parent->new_rate;
+
+ goto out;
+ }
+
+ new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
+
+ if (best_parent_rate != clk->parent->rate) {
+ top = clk_calc_new_rates(clk->parent, best_parent_rate);
+
+ goto out;
+ }
+
+out:
+ clk_calc_subtree(clk, new_rate);
+
+ return top;
+}
+
+/*
+ * Notify about rate changes in a subtree. Always walk down the whole tree
+ * so that in case of an error we can walk down the whole tree again and
+ * abort the change.
+ */
+static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
+{
+ struct clk *child, *fail_clk = NULL;
+ int ret = NOTIFY_DONE;
+
+ if (clk->rate == clk->new_rate)
+ return NULL;
+
+ if (clk->notifier_count) {
+ ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
+ if (ret & NOTIFY_STOP_MASK)
+ fail_clk = clk;
+ }
+
+ hlist_for_each_entry(child, &clk->children, child_node) {
+ clk = clk_propagate_rate_change(child, event);
+ if (clk)
+ fail_clk = clk;
+ }
+
+ return fail_clk;
+}
+
+/*
+ * walk down a subtree and set the new rates notifying the rate
+ * change on the way
+ */
+static void clk_change_rate(struct clk *clk)
+{
+ struct clk *child;
+ unsigned long old_rate;
+ unsigned long best_parent_rate = 0;
+
+ old_rate = clk->rate;
+
+ if (clk->parent)
+ best_parent_rate = clk->parent->rate;
+
+ if (clk->ops->set_rate)
+ clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw, best_parent_rate);
+ else
+ clk->rate = best_parent_rate;
+
+ if (clk->notifier_count && old_rate != clk->rate)
+ __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
+
+ hlist_for_each_entry(child, &clk->children, child_node)
+ clk_change_rate(child);
+}
+
+/**
+ * clk_set_rate - specify a new rate for clk
+ * @clk: the clk whose rate is being changed
+ * @rate: the new rate for clk
+ *
+ * In the simplest case clk_set_rate will only adjust the rate of clk.
+ *
+ * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to
+ * propagate up to clk's parent; whether or not this happens depends on the
+ * outcome of clk's .round_rate implementation. If *parent_rate is unchanged
+ * after calling .round_rate then upstream parent propagation is ignored. If
+ * *parent_rate comes back with a new rate for clk's parent then we propagate
+ * up to clk's parent and set it's rate. Upward propagation will continue
+ * until either a clk does not support the CLK_SET_RATE_PARENT flag or
+ * .round_rate stops requesting changes to clk's parent_rate.
+ *
+ * Rate changes are accomplished via tree traversal that also recalculates the
+ * rates for the clocks and fires off POST_RATE_CHANGE notifiers.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *top, *fail_clk;
+ int ret = 0;
+
+ /* prevent racing with updates to the clock topology */
+ clk_prepare_lock();
+
+ /* bail early if nothing to do */
+ if (rate == clk->rate)
+ goto out;
+
+ if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* calculate new rates and get the topmost changed clock */
+ top = clk_calc_new_rates(clk, rate);
+ if (!top) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* notify that we are about to change rates */
+ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
+ if (fail_clk) {
+ pr_warn("%s: failed to set %s rate\n", __func__,
+ fail_clk->name);
+ clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* change the rates */
+ clk_change_rate(top);
+
+out:
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+/**
+ * clk_get_parent - return the parent of a clk
+ * @clk: the clk whose parent gets returned
+ *
+ * Simply returns clk->parent. Returns NULL if clk is NULL.
+ */
+struct clk *clk_get_parent(struct clk *clk)
+{
+ struct clk *parent;
+
+ clk_prepare_lock();
+ parent = __clk_get_parent(clk);
+ clk_prepare_unlock();
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
+/*
+ * .get_parent is mandatory for clocks with multiple possible parents. It is
+ * optional for single-parent clocks. Always call .get_parent if it is
+ * available and WARN if it is missing for multi-parent clocks.
+ *
+ * For single-parent clocks without .get_parent, first check to see if the
+ * .parents array exists, and if so use it to avoid an expensive tree
+ * traversal. If .parents does not exist then walk the tree with __clk_lookup.
+ */
+static struct clk *__clk_init_parent(struct clk *clk)
+{
+ struct clk *ret = NULL;
+ u8 index;
+
+ /* handle the trivial cases */
+
+ if (!clk->num_parents)
+ goto out;
+
+ if (clk->num_parents == 1) {
+ if (IS_ERR_OR_NULL(clk->parent))
+ ret = clk->parent = __clk_lookup(clk->parent_names[0]);
+ ret = clk->parent;
+ goto out;
+ }
+
+ if (!clk->ops->get_parent) {
+ WARN(!clk->ops->get_parent,
+ "%s: multi-parent clocks must implement .get_parent\n",
+ __func__);
+ goto out;
+ };
+
+ /*
+ * Do our best to cache parent clocks in clk->parents. This prevents
+ * unnecessary and expensive calls to __clk_lookup. We don't set
+ * clk->parent here; that is done by the calling function
+ */
+
+ index = clk->ops->get_parent(clk->hw);
+
+ if (!clk->parents)
+ clk->parents =
+ kzalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+
+ if (!clk->parents)
+ ret = __clk_lookup(clk->parent_names[index]);
+ else if (!clk->parents[index])
+ ret = clk->parents[index] =
+ __clk_lookup(clk->parent_names[index]);
+ else
+ ret = clk->parents[index];
+
+out:
+ return ret;
+}
+
+static void clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+ hlist_del(&clk->child_node);
+
+ if (new_parent)
+ hlist_add_head(&clk->child_node, &new_parent->children);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+ clk->parent = new_parent;
+}
+
+void __clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+ clk_reparent(clk, new_parent);
+ clk_debug_reparent(clk, new_parent);
+ __clk_recalc_rates(clk, POST_RATE_CHANGE);
+}
+
+static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
+{
+ u8 i;
+
+ if (!clk->parents)
+ clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+
+ /*
+ * find index of new parent clock using cached parent ptrs,
+ * or if not yet cached, use string name comparison and cache
+ * them now to avoid future calls to __clk_lookup.
+ */
+ for (i = 0; i < clk->num_parents; i++) {
+ if (clk->parents && clk->parents[i] == parent)
+ break;
+ else if (!strcmp(clk->parent_names[i], parent->name)) {
+ if (clk->parents)
+ clk->parents[i] = __clk_lookup(parent->name);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct clk *old_parent = clk->parent;
+ bool migrated_enable = false;
+
+ /* migrate prepare */
+ if (clk->prepare_count)
+ __clk_prepare(parent);
+
+ flags = clk_enable_lock();
+
+ /* migrate enable */
+ if (clk->enable_count) {
+ __clk_enable(parent);
+ migrated_enable = true;
+ }
+
+ /* update the clk tree topology */
+ clk_reparent(clk, parent);
+
+ clk_enable_unlock(flags);
+
+ /* change clock input source */
+ if (parent && clk->ops->set_parent)
+ ret = clk->ops->set_parent(clk->hw, p_index);
+
+ if (ret) {
+ /*
+ * The error handling is tricky due to that we need to release
+ * the spinlock while issuing the .set_parent callback. This
+ * means the new parent might have been enabled/disabled in
+ * between, which must be considered when doing rollback.
+ */
+ flags = clk_enable_lock();
+
+ clk_reparent(clk, old_parent);
+
+ if (migrated_enable && clk->enable_count) {
+ __clk_disable(parent);
+ } else if (migrated_enable && (clk->enable_count == 0)) {
+ __clk_disable(old_parent);
+ } else if (!migrated_enable && clk->enable_count) {
+ __clk_disable(parent);
+ __clk_enable(old_parent);
+ }
+
+ clk_enable_unlock(flags);
+
+ if (clk->prepare_count)
+ __clk_unprepare(parent);
+
+ return ret;
+ }
+
+ /* clean up enable for old parent if migration was done */
+ if (migrated_enable) {
+ flags = clk_enable_lock();
+ __clk_disable(old_parent);
+ clk_enable_unlock(flags);
+ }
+
+ /* clean up prepare for old parent if migration was done */
+ if (clk->prepare_count)
+ __clk_unprepare(old_parent);
+
+ /* update debugfs with new clk tree topology */
+ clk_debug_reparent(clk, parent);
+ return 0;
+}
+
+/**
+ * clk_set_parent - switch the parent of a mux clk
+ * @clk: the mux clk whose input we are switching
+ * @parent: the new input to clk
+ *
+ * Re-parent clk to use parent as it's new input source. If clk has the
+ * CLK_SET_PARENT_GATE flag set then clk must be gated for this
+ * operation to succeed. After successfully changing clk's parent
+ * clk_set_parent will update the clk topology, sysfs topology and
+ * propagate rate recalculation via __clk_recalc_rates. Returns 0 on
+ * success, -EERROR otherwise.
+ */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = 0;
+ u8 p_index = 0;
+ unsigned long p_rate = 0;
+
+ if (!clk || !clk->ops)
+ return -EINVAL;
+
+ /* verify ops for for multi-parent clks */
+ if ((clk->num_parents > 1) && (!clk->ops->set_parent))
+ return -ENOSYS;
+
+ /* prevent racing with updates to the clock topology */
+ clk_prepare_lock();
+
+ if (clk->parent == parent)
+ goto out;
+
+ /* check that we are allowed to re-parent if the clock is in use */
+ if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* try finding the new parent index */
+ if (parent) {
+ p_index = clk_fetch_parent_index(clk, parent);
+ p_rate = parent->rate;
+ if (p_index == clk->num_parents) {
+ pr_debug("%s: clk %s can not be parent of clk %s\n",
+ __func__, parent->name, clk->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* propagate PRE_RATE_CHANGE notifications */
+ if (clk->notifier_count)
+ ret = __clk_speculate_rates(clk, p_rate);
+
+ /* abort if a driver objects */
+ if (ret & NOTIFY_STOP_MASK)
+ goto out;
+
+ /* do the re-parent */
+ ret = __clk_set_parent(clk, parent, p_index);
+
+ /* propagate rate recalculation accordingly */
+ if (ret)
+ __clk_recalc_rates(clk, ABORT_RATE_CHANGE);
+ else
+ __clk_recalc_rates(clk, POST_RATE_CHANGE);
+
+out:
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+/**
+ * __clk_init - initialize the data structures in a struct clk
+ * @dev: device initializing this clk, placeholder for now
+ * @clk: clk being initialized
+ *
+ * Initializes the lists in struct clk, queries the hardware for the
+ * parent and rate and sets them both.
+ */
+int __clk_init(struct device *dev, struct clk *clk)
+{
+ int i, ret = 0;
+ struct clk *orphan;
+ struct hlist_node *tmp2;
+
+ if (!clk)
+ return -EINVAL;
+
+ clk_prepare_lock();
+
+ /* check to see if a clock with this name is already registered */
+ if (__clk_lookup(clk->name)) {
+ pr_debug("%s: clk %s already initialized\n",
+ __func__, clk->name);
+ ret = -EEXIST;
+ goto out;
+ }
+
+ /* check that clk_ops are sane. See Documentation/clk.txt */
+ if (clk->ops->set_rate &&
+ !(clk->ops->round_rate && clk->ops->recalc_rate)) {
+ pr_warning("%s: %s must implement .round_rate & .recalc_rate\n",
+ __func__, clk->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (clk->ops->set_parent && !clk->ops->get_parent) {
+ pr_warning("%s: %s must implement .get_parent & .set_parent\n",
+ __func__, clk->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* throw a WARN if any entries in parent_names are NULL */
+ for (i = 0; i < clk->num_parents; i++)
+ WARN(!clk->parent_names[i],
+ "%s: invalid NULL in %s's .parent_names\n",
+ __func__, clk->name);
+
+ /*
+ * Allocate an array of struct clk *'s to avoid unnecessary string
+ * look-ups of clk's possible parents. This can fail for clocks passed
+ * in to clk_init during early boot; thus any access to clk->parents[]
+ * must always check for a NULL pointer and try to populate it if
+ * necessary.
+ *
+ * If clk->parents is not NULL we skip this entire block. This allows
+ * for clock drivers to statically initialize clk->parents.
+ */
+ if (clk->num_parents > 1 && !clk->parents) {
+ clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+ /*
+ * __clk_lookup returns NULL for parents that have not been
+ * clk_init'd; thus any access to clk->parents[] must check
+ * for a NULL pointer. We can always perform lazy lookups for
+ * missing parents later on.
+ */
+ if (clk->parents)
+ for (i = 0; i < clk->num_parents; i++)
+ clk->parents[i] =
+ __clk_lookup(clk->parent_names[i]);
+ }
+
+ clk->parent = __clk_init_parent(clk);
+
+ /*
+ * Populate clk->parent if parent has already been __clk_init'd. If
+ * parent has not yet been __clk_init'd then place clk in the orphan
+ * list. If clk has set the CLK_IS_ROOT flag then place it in the root
+ * clk list.
+ *
+ * Every time a new clk is clk_init'd then we walk the list of orphan
+ * clocks and re-parent any that are children of the clock currently
+ * being clk_init'd.
+ */
+ if (clk->parent)
+ hlist_add_head(&clk->child_node,
+ &clk->parent->children);
+ else if (clk->flags & CLK_IS_ROOT)
+ hlist_add_head(&clk->child_node, &clk_root_list);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+ /*
+ * Set clk's rate. The preferred method is to use .recalc_rate. For
+ * simple clocks and lazy developers the default fallback is to use the
+ * parent's rate. If a clock doesn't have a parent (or is orphaned)
+ * then rate is set to zero.
+ */
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw,
+ __clk_get_rate(clk->parent));
+ else if (clk->parent)
+ clk->rate = clk->parent->rate;
+ else
+ clk->rate = 0;
+
+ /*
+ * walk the list of orphan clocks and reparent any that are children of
+ * this clock
+ */
+ hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
+ if (orphan->ops->get_parent) {
+ i = orphan->ops->get_parent(orphan->hw);
+ if (!strcmp(clk->name, orphan->parent_names[i]))
+ __clk_reparent(orphan, clk);
+ continue;
+ }
+
+ for (i = 0; i < orphan->num_parents; i++)
+ if (!strcmp(clk->name, orphan->parent_names[i])) {
+ __clk_reparent(orphan, clk);
+ break;
+ }
+ }
+
+ /*
+ * optional platform-specific magic
+ *
+ * The .init callback is not used by any of the basic clock types, but
+ * exists for weird hardware that must perform initialization magic.
+ * Please consider other ways of solving initialization problems before
+ * using this callback, as it's use is discouraged.
+ */
+ if (clk->ops->init)
+ clk->ops->init(clk->hw);
+
+ clk_debug_register(clk);
+
+out:
+ clk_prepare_unlock();
+
+ return ret;
+}
+
+/**
+ * __clk_register - register a clock and return a cookie.
+ *
+ * Same as clk_register, except that the .clk field inside hw shall point to a
+ * preallocated (generally statically allocated) struct clk. None of the fields
+ * of the struct clk need to be initialized.
+ *
+ * The data pointed to by .init and .clk field shall NOT be marked as init
+ * data.
+ *
+ * __clk_register is only exposed via clk-private.h and is intended for use with
+ * very large numbers of clocks that need to be statically initialized. It is
+ * a layering violation to include clk-private.h from any code which implements
+ * a clock's .ops; as such any statically initialized clock data MUST be in a
+ * separate C file from the logic that implements it's operations. Returns 0
+ * on success, otherwise an error code.
+ */
+struct clk *__clk_register(struct device *dev, struct clk_hw *hw)
+{
+ int ret;
+ struct clk *clk;
+
+ clk = hw->clk;
+ clk->name = hw->init->name;
+ clk->ops = hw->init->ops;
+ clk->hw = hw;
+ clk->flags = hw->init->flags;
+ clk->parent_names = hw->init->parent_names;
+ clk->num_parents = hw->init->num_parents;
+
+ ret = __clk_init(dev, clk);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(__clk_register);
+
+static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
+{
+ int i, ret;
+
+ clk->name = kstrdup(hw->init->name, GFP_KERNEL);
+ if (!clk->name) {
+ pr_err("%s: could not allocate clk->name\n", __func__);
+ ret = -ENOMEM;
+ goto fail_name;
+ }
+ clk->ops = hw->init->ops;
+ clk->hw = hw;
+ clk->flags = hw->init->flags;
+ clk->num_parents = hw->init->num_parents;
+ hw->clk = clk;
+
+ /* allocate local copy in case parent_names is __initdata */
+ clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents),
+ GFP_KERNEL);
+
+ if (!clk->parent_names) {
+ pr_err("%s: could not allocate clk->parent_names\n", __func__);
+ ret = -ENOMEM;
+ goto fail_parent_names;
+ }
+
+
+ /* copy each string name in case parent_names is __initdata */
+ for (i = 0; i < clk->num_parents; i++) {
+ clk->parent_names[i] = kstrdup(hw->init->parent_names[i],
+ GFP_KERNEL);
+ if (!clk->parent_names[i]) {
+ pr_err("%s: could not copy parent_names\n", __func__);
+ ret = -ENOMEM;
+ goto fail_parent_names_copy;
+ }
+ }
+
+ ret = __clk_init(dev, clk);
+ if (!ret)
+ return 0;
+
+fail_parent_names_copy:
+ while (--i >= 0)
+ kfree(clk->parent_names[i]);
+ kfree(clk->parent_names);
+fail_parent_names:
+ kfree(clk->name);
+fail_name:
+ return ret;
+}
+
+/**
+ * clk_register - allocate a new clock, register it and return an opaque cookie
+ * @dev: device that is registering this clock
+ * @hw: link to hardware-specific clock data
+ *
+ * clk_register is the primary interface for populating the clock tree with new
+ * clock nodes. It returns a pointer to the newly allocated struct clk which
+ * cannot be dereferenced by driver code but may be used in conjuction with the
+ * rest of the clock API. In the event of an error clk_register will return an
+ * error code; drivers must test for an error code after calling clk_register.
+ */
+struct clk *clk_register(struct device *dev, struct clk_hw *hw)
+{
+ int ret;
+ struct clk *clk;
+
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+ if (!clk) {
+ pr_err("%s: could not allocate clk\n", __func__);
+ ret = -ENOMEM;
+ goto fail_out;
+ }
+
+ ret = _clk_register(dev, hw, clk);
+ if (!ret)
+ return clk;
+
+ kfree(clk);
+fail_out:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(clk_register);
+
+/**
+ * clk_unregister - unregister a currently registered clock
+ * @clk: clock to unregister
+ *
+ * Currently unimplemented.
+ */
+void clk_unregister(struct clk *clk) {}
+EXPORT_SYMBOL_GPL(clk_unregister);
+
+static void devm_clk_release(struct device *dev, void *res)
+{
+ clk_unregister(res);
+}
+
+/**
+ * devm_clk_register - resource managed clk_register()
+ * @dev: device that is registering this clock
+ * @hw: link to hardware-specific clock data
+ *
+ * Managed clk_register(). Clocks returned from this function are
+ * automatically clk_unregister()ed on driver detach. See clk_register() for
+ * more information.
+ */
+struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = devres_alloc(devm_clk_release, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return ERR_PTR(-ENOMEM);
+
+ ret = _clk_register(dev, hw, clk);
+ if (!ret) {
+ devres_add(dev, clk);
+ } else {
+ devres_free(clk);
+ clk = ERR_PTR(ret);
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(devm_clk_register);
+
+static int devm_clk_match(struct device *dev, void *res, void *data)
+{
+ struct clk *c = res;
+ if (WARN_ON(!c))
+ return 0;
+ return c == data;
+}
+
+/**
+ * devm_clk_unregister - resource managed clk_unregister()
+ * @clk: clock to unregister
+ *
+ * Deallocate a clock allocated with devm_clk_register(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_clk_unregister(struct device *dev, struct clk *clk)
+{
+ WARN_ON(devres_release(dev, devm_clk_release, devm_clk_match, clk));
+}
+EXPORT_SYMBOL_GPL(devm_clk_unregister);
+
+/*** clk rate change notifiers ***/
+
+/**
+ * clk_notifier_register - add a clk rate change notifier
+ * @clk: struct clk * to watch
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request notification when clk's rate changes. This uses an SRCU
+ * notifier because we want it to block and notifier unregistrations are
+ * uncommon. The callbacks associated with the notifier must not
+ * re-enter into the clk framework by calling any top-level clk APIs;
+ * this will cause a nested prepare_lock mutex.
+ *
+ * Pre-change notifier callbacks will be passed the current, pre-change
+ * rate of the clk via struct clk_notifier_data.old_rate. The new,
+ * post-change rate of the clk is passed via struct
+ * clk_notifier_data.new_rate.
+ *
+ * Post-change notifiers will pass the now-current, post-change rate of
+ * the clk in both struct clk_notifier_data.old_rate and struct
+ * clk_notifier_data.new_rate.
+ *
+ * Abort-change notifiers are effectively the opposite of pre-change
+ * notifiers: the original pre-change clk rate is passed in via struct
+ * clk_notifier_data.new_rate and the failed post-change rate is passed
+ * in via struct clk_notifier_data.old_rate.
+ *
+ * clk_notifier_register() must be called from non-atomic context.
+ * Returns -EINVAL if called with null arguments, -ENOMEM upon
+ * allocation failure; otherwise, passes along the return value of
+ * srcu_notifier_chain_register().
+ */
+int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+ struct clk_notifier *cn;
+ int ret = -ENOMEM;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ clk_prepare_lock();
+
+ /* search the list of notifiers for this clk */
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ /* if clk wasn't in the notifier list, allocate new clk_notifier */
+ if (cn->clk != clk) {
+ cn = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
+ if (!cn)
+ goto out;
+
+ cn->clk = clk;
+ srcu_init_notifier_head(&cn->notifier_head);
+
+ list_add(&cn->node, &clk_notifier_list);
+ }
+
+ ret = srcu_notifier_chain_register(&cn->notifier_head, nb);
+
+ clk->notifier_count++;
+
+out:
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_notifier_register);
+
+/**
+ * clk_notifier_unregister - remove a clk rate change notifier
+ * @clk: struct clk *
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request no further notification for changes to 'clk' and frees memory
+ * allocated in clk_notifier_register.
+ *
+ * Returns -EINVAL if called with null arguments; otherwise, passes
+ * along the return value of srcu_notifier_chain_unregister().
+ */
+int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
+{
+ struct clk_notifier *cn = NULL;
+ int ret = -EINVAL;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ clk_prepare_lock();
+
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ if (cn->clk == clk) {
+ ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);
+
+ clk->notifier_count--;
+
+ /* XXX the notifier code should handle this better */
+ if (!cn->notifier_head.head) {
+ srcu_cleanup_notifier_head(&cn->notifier_head);
+ list_del(&cn->node);
+ kfree(cn);
+ }
+
+ } else {
+ ret = -ENOENT;
+ }
+
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_notifier_unregister);
+
+#ifdef CONFIG_OF
+/**
+ * struct of_clk_provider - Clock provider registration structure
+ * @link: Entry in global list of clock providers
+ * @node: Pointer to device tree node of clock provider
+ * @get: Get clock callback. Returns NULL or a struct clk for the
+ * given clock specifier
+ * @data: context pointer to be passed into @get callback
+ */
+struct of_clk_provider {
+ struct list_head link;
+
+ struct device_node *node;
+ struct clk *(*get)(struct of_phandle_args *clkspec, void *data);
+ void *data;
+};
+
+extern struct of_device_id __clk_of_table[];
+
+static const struct of_device_id __clk_of_table_sentinel
+ __used __section(__clk_of_table_end);
+
+static LIST_HEAD(of_clk_providers);
+static DEFINE_MUTEX(of_clk_lock);
+
+struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ return data;
+}
+EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
+
+struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct clk_onecell_data *clk_data = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= clk_data->clk_num) {
+ pr_err("%s: invalid clock index %d\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return clk_data->clks[idx];
+}
+EXPORT_SYMBOL_GPL(of_clk_src_onecell_get);
+
+/**
+ * of_clk_add_provider() - Register a clock provider for a node
+ * @np: Device node pointer associated with clock provider
+ * @clk_src_get: callback for decoding clock
+ * @data: context pointer for @clk_src_get callback.
+ */
+int of_clk_add_provider(struct device_node *np,
+ struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
+ void *data),
+ void *data)
+{
+ struct of_clk_provider *cp;
+
+ cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->node = of_node_get(np);
+ cp->data = data;
+ cp->get = clk_src_get;
+
+ mutex_lock(&of_clk_lock);
+ list_add(&cp->link, &of_clk_providers);
+ mutex_unlock(&of_clk_lock);
+ pr_debug("Added clock from %s\n", np->full_name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_clk_add_provider);
+
+/**
+ * of_clk_del_provider() - Remove a previously registered clock provider
+ * @np: Device node pointer associated with clock provider
+ */
+void of_clk_del_provider(struct device_node *np)
+{
+ struct of_clk_provider *cp;
+
+ mutex_lock(&of_clk_lock);
+ list_for_each_entry(cp, &of_clk_providers, link) {
+ if (cp->node == np) {
+ list_del(&cp->link);
+ of_node_put(cp->node);
+ kfree(cp);
+ break;
+ }
+ }
+ mutex_unlock(&of_clk_lock);
+}
+EXPORT_SYMBOL_GPL(of_clk_del_provider);
+
+struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+{
+ struct of_clk_provider *provider;
+ struct clk *clk = ERR_PTR(-ENOENT);
+
+ /* Check if we have such a provider in our array */
+ mutex_lock(&of_clk_lock);
+ list_for_each_entry(provider, &of_clk_providers, link) {
+ if (provider->node == clkspec->np)
+ clk = provider->get(clkspec, provider->data);
+ if (!IS_ERR(clk))
+ break;
+ }
+ mutex_unlock(&of_clk_lock);
+
+ return clk;
+}
+
+const char *of_clk_get_parent_name(struct device_node *np, int index)
+{
+ struct of_phandle_args clkspec;
+ const char *clk_name;
+ int rc;
+
+ if (index < 0)
+ return NULL;
+
+ rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
+ &clkspec);
+ if (rc)
+ return NULL;
+
+ if (of_property_read_string_index(clkspec.np, "clock-output-names",
+ clkspec.args_count ? clkspec.args[0] : 0,
+ &clk_name) < 0)
+ clk_name = clkspec.np->name;
+
+ of_node_put(clkspec.np);
+ return clk_name;
+}
+EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
+
+/**
+ * of_clk_init() - Scan and init clock providers from the DT
+ * @matches: array of compatible values and init functions for providers.
+ *
+ * This function scans the device tree for matching clock providers and
+ * calls their initialization functions
+ */
+void __init of_clk_init(const struct of_device_id *matches)
+{
+ struct device_node *np;
+
+ if (!matches)
+ matches = __clk_of_table;
+
+ for_each_matching_node(np, matches) {
+ const struct of_device_id *match = of_match_node(matches, np);
+ of_clk_init_cb_t clk_init_cb = match->data;
+ clk_init_cb(np);
+ }
+}
+#endif
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
new file mode 100644
index 000000000..442a31363
--- /dev/null
+++ b/drivers/clk/clkdev.c
@@ -0,0 +1,329 @@
+/*
+ * drivers/clk/clkdev.c
+ *
+ * Copyright (C) 2008 Russell King.
+ *
+ * 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.
+ *
+ * Helper for the clk API to assist looking up a struct clk.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
+struct clk *of_clk_get(struct device_node *np, int index)
+{
+ struct of_phandle_args clkspec;
+ struct clk *clk;
+ int rc;
+
+ if (index < 0)
+ return ERR_PTR(-EINVAL);
+
+ rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
+ &clkspec);
+ if (rc)
+ return ERR_PTR(rc);
+
+ clk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
+ return clk;
+}
+EXPORT_SYMBOL(of_clk_get);
+
+/**
+ * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node
+ * @np: pointer to clock consumer node
+ * @name: name of consumer's clock input, or NULL for the first clock reference
+ *
+ * This function parses the clocks and clock-names properties,
+ * and uses them to look up the struct clk from the registered list of clock
+ * providers.
+ */
+struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
+{
+ struct clk *clk = ERR_PTR(-ENOENT);
+
+ /* Walk up the tree of devices looking for a clock that matches */
+ while (np) {
+ int index = 0;
+
+ /*
+ * For named clocks, first look up the name in the
+ * "clock-names" property. If it cannot be found, then
+ * index will be an error code, and of_clk_get() will fail.
+ */
+ if (name)
+ index = of_property_match_string(np, "clock-names", name);
+ clk = of_clk_get(np, index);
+ if (!IS_ERR(clk))
+ break;
+ else if (name && index >= 0) {
+ pr_err("ERROR: could not get clock %s:%s(%i)\n",
+ np->full_name, name ? name : "", index);
+ return clk;
+ }
+
+ /*
+ * No matching clock found on this node. If the parent node
+ * has a "clock-ranges" property, then we can try one of its
+ * clocks.
+ */
+ np = np->parent;
+ if (np && !of_get_property(np, "clock-ranges", NULL))
+ break;
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL(of_clk_get_by_name);
+#endif
+
+/*
+ * Find the correct struct clk for the device and connection ID.
+ * We do slightly fuzzy matching here:
+ * An entry with a NULL ID is assumed to be a wildcard.
+ * If an entry has a device ID, it must match
+ * If an entry has a connection ID, it must match
+ * Then we take the most specific entry - with the following
+ * order of precedence: dev+con > dev only > con only.
+ */
+static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
+{
+ struct clk_lookup *p, *cl = NULL;
+ int match, best_found = 0, best_possible = 0;
+
+ if (dev_id)
+ best_possible += 2;
+ if (con_id)
+ best_possible += 1;
+
+ list_for_each_entry(p, &clocks, node) {
+ match = 0;
+ if (p->dev_id) {
+ if (!dev_id || strcmp(p->dev_id, dev_id))
+ continue;
+ match += 2;
+ }
+ if (p->con_id) {
+ if (!con_id || strcmp(p->con_id, con_id))
+ continue;
+ match += 1;
+ }
+
+ if (match > best_found) {
+ cl = p;
+ if (match != best_possible)
+ best_found = match;
+ else
+ break;
+ }
+ }
+ return cl;
+}
+
+struct clk *clk_get_sys(const char *dev_id, const char *con_id)
+{
+ struct clk_lookup *cl;
+
+ mutex_lock(&clocks_mutex);
+ cl = clk_find(dev_id, con_id);
+ if (cl && !__clk_get(cl->clk))
+ cl = NULL;
+ mutex_unlock(&clocks_mutex);
+
+ return cl ? cl->clk : ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get_sys);
+
+struct clk *clk_get(struct device *dev, const char *con_id)
+{
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+ struct clk *clk;
+
+ if (dev) {
+ clk = of_clk_get_by_name(dev->of_node, con_id);
+ if (!IS_ERR(clk) && __clk_get(clk))
+ return clk;
+ }
+
+ return clk_get_sys(dev_id, con_id);
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ __clk_put(clk);
+}
+EXPORT_SYMBOL(clk_put);
+
+void clkdev_add(struct clk_lookup *cl)
+{
+ mutex_lock(&clocks_mutex);
+ list_add_tail(&cl->node, &clocks);
+ mutex_unlock(&clocks_mutex);
+}
+EXPORT_SYMBOL(clkdev_add);
+
+void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
+{
+ mutex_lock(&clocks_mutex);
+ while (num--) {
+ list_add_tail(&cl->node, &clocks);
+ cl++;
+ }
+ mutex_unlock(&clocks_mutex);
+}
+
+#define MAX_DEV_ID 20
+#define MAX_CON_ID 16
+
+struct clk_lookup_alloc {
+ struct clk_lookup cl;
+ char dev_id[MAX_DEV_ID];
+ char con_id[MAX_CON_ID];
+};
+
+static struct clk_lookup * __init_refok
+vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
+ va_list ap)
+{
+ struct clk_lookup_alloc *cla;
+
+ cla = __clkdev_alloc(sizeof(*cla));
+ if (!cla)
+ return NULL;
+
+ cla->cl.clk = clk;
+ if (con_id) {
+ strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
+ cla->cl.con_id = cla->con_id;
+ }
+
+ if (dev_fmt) {
+ vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
+ cla->cl.dev_id = cla->dev_id;
+ }
+
+ return &cla->cl;
+}
+
+struct clk_lookup * __init_refok
+clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
+{
+ struct clk_lookup *cl;
+ va_list ap;
+
+ va_start(ap, dev_fmt);
+ cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
+ va_end(ap);
+
+ return cl;
+}
+EXPORT_SYMBOL(clkdev_alloc);
+
+int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
+ struct device *dev)
+{
+ struct clk *r = clk_get(dev, id);
+ struct clk_lookup *l;
+
+ if (IS_ERR(r))
+ return PTR_ERR(r);
+
+ l = clkdev_alloc(r, alias, alias_dev_name);
+ clk_put(r);
+ if (!l)
+ return -ENODEV;
+ clkdev_add(l);
+ return 0;
+}
+EXPORT_SYMBOL(clk_add_alias);
+
+/*
+ * clkdev_drop - remove a clock dynamically allocated
+ */
+void clkdev_drop(struct clk_lookup *cl)
+{
+ mutex_lock(&clocks_mutex);
+ list_del(&cl->node);
+ mutex_unlock(&clocks_mutex);
+ kfree(cl);
+}
+EXPORT_SYMBOL(clkdev_drop);
+
+/**
+ * clk_register_clkdev - register one clock lookup for a struct clk
+ * @clk: struct clk to associate with all clk_lookups
+ * @con_id: connection ID string on device
+ * @dev_id: format string describing device name
+ *
+ * con_id or dev_id may be NULL as a wildcard, just as in the rest of
+ * clkdev.
+ *
+ * To make things easier for mass registration, we detect error clks
+ * from a previous clk_register() call, and return the error code for
+ * those. This is to permit this function to be called immediately
+ * after clk_register().
+ */
+int clk_register_clkdev(struct clk *clk, const char *con_id,
+ const char *dev_fmt, ...)
+{
+ struct clk_lookup *cl;
+ va_list ap;
+
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ va_start(ap, dev_fmt);
+ cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
+ va_end(ap);
+
+ if (!cl)
+ return -ENOMEM;
+
+ clkdev_add(cl);
+
+ return 0;
+}
+
+/**
+ * clk_register_clkdevs - register a set of clk_lookup for a struct clk
+ * @clk: struct clk to associate with all clk_lookups
+ * @cl: array of clk_lookup structures with con_id and dev_id pre-initialized
+ * @num: number of clk_lookup structures to register
+ *
+ * To make things easier for mass registration, we detect error clks
+ * from a previous clk_register() call, and return the error code for
+ * those. This is to permit this function to be called immediately
+ * after clk_register().
+ */
+int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num)
+{
+ unsigned i;
+
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ for (i = 0; i < num; i++, cl++) {
+ cl->clk = clk;
+ clkdev_add(cl);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_register_clkdevs);
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
new file mode 100755
index 000000000..3f45100c7
--- /dev/null
+++ b/drivers/clk/mediatek/Makefile
@@ -0,0 +1,2 @@
+obj-y += clk-mtk.o clk-pll.o clk-gate.o
+obj-$(CONFIG_ARCH_MT6735) += clk-mt6735.o clk-mt6735-pll.o clk-mt6735-pg.o
diff --git a/drivers/clk/mediatek/clk-debug.h b/drivers/clk/mediatek/clk-debug.h
new file mode 100644
index 000000000..076157470
--- /dev/null
+++ b/drivers/clk/mediatek/clk-debug.h
@@ -0,0 +1,29 @@
+#ifndef __DRV_CLK_DEBUG_H
+#define __DRV_CLK_DEBUG_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+#ifdef CLK_DEBUG
+#undef CLK_DEBUG
+#endif
+
+#define CLK_DEBUG 1
+
+#define TRACE(fmt, args...) \
+ pr_warn("[clk-mtk] %s():%d: " fmt, __func__, __LINE__, ##args)
+
+#define PR_ERR(fmt, args...) pr_warn("%s(): " fmt, __func__, ##args)
+
+#ifdef pr_debug
+#undef pr_debug
+#define pr_debug TRACE
+#endif
+
+#ifdef pr_err
+#undef pr_err
+#define pr_err PR_ERR
+#endif
+
+#endif /* __DRV_CLK_DEBUG_H */
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
new file mode 100644
index 000000000..61631be25
--- /dev/null
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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/of.h>
+#include <linux/of_address.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+/*
+ * clk_gate
+ */
+
+static void cg_set_mask(struct mtk_clk_gate *cg, u32 mask)
+{
+ u32 r;
+
+ if (cg->flags & CLK_GATE_NO_SETCLR_REG) {
+ r = readl_relaxed(cg->sta_addr) | mask;
+ writel_relaxed(r, cg->sta_addr);
+ } else
+ writel_relaxed(mask, cg->set_addr);
+}
+
+static void cg_clr_mask(struct mtk_clk_gate *cg, u32 mask)
+{
+ u32 r;
+
+ if (cg->flags & CLK_GATE_NO_SETCLR_REG) {
+ r = readl_relaxed(cg->sta_addr) & ~mask;
+ writel_relaxed(r, cg->sta_addr);
+ } else
+ writel_relaxed(mask, cg->clr_addr);
+}
+
+static int cg_enable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+ u32 mask = BIT(cg->bit);
+#ifdef Bring_Up
+ if (printk_ratelimit())
+ pr_notice("[CCF] %s: %s, bit: %u\n", __func__, __clk_get_name(hw->clk),
+ cg->bit);
+ return 0;
+#endif
+
+ pr_debug("%s, bit: %u\n", __clk_get_name(hw->clk), cg->bit);
+
+ mtk_clk_lock(flags);
+
+ if (cg->flags & CLK_GATE_INVERSE)
+ cg_set_mask(cg, mask);
+ else
+ cg_clr_mask(cg, mask);
+
+ mtk_clk_unlock(flags);
+
+ return 0;
+}
+
+static void cg_disable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+ u32 mask = BIT(cg->bit);
+#ifdef Bring_Up
+ if (printk_ratelimit())
+ pr_notice("[CCF] %s: %s, bit: %u\n", __func__, __clk_get_name(hw->clk),
+ cg->bit);
+ return;
+#endif
+
+ pr_debug("%s, bit: %u\n", __clk_get_name(hw->clk), cg->bit);
+
+ mtk_clk_lock(flags);
+
+ if (cg->flags & CLK_GATE_INVERSE)
+ cg_clr_mask(cg, mask);
+ else
+ cg_set_mask(cg, mask);
+
+ mtk_clk_unlock(flags);
+}
+
+static int cg_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_gate *cg = to_clk_gate(hw);
+ u32 mask;
+ u32 val;
+ int r;
+#ifdef Bring_Up
+ if (printk_ratelimit())
+ pr_notice("[CCF] %s: %s\n", __func__, __clk_get_name(hw->clk));
+ return 1;
+#endif
+
+ mask = BIT(cg->bit);
+ val = mask & readl(cg->sta_addr);
+
+ r = (cg->flags & CLK_GATE_INVERSE) ? (val != 0) : (val == 0);
+
+ pr_debug("%d, %s, bit[%d]\n", r, __clk_get_name(hw->clk), (int)cg->bit);
+
+ return r;
+}
+
+static const struct clk_ops mtk_clk_gate_ops = {
+ .is_enabled = cg_is_enabled,
+ .enable = cg_enable,
+ .disable = cg_disable,
+};
+
+struct clk *mtk_clk_register_gate(
+ const char *name,
+ const char *parent_name,
+ void __iomem *set_addr,
+ void __iomem *clr_addr,
+ void __iomem *sta_addr,
+ u8 bit,
+ u32 flags)
+{
+ struct mtk_clk_gate *cg;
+ struct clk *clk;
+ struct clk_init_data init;
+
+#ifdef Bring_Up
+ pr_notice("[CCF] name: %s, bit: %d\n", name, (int)bit);
+#else
+ pr_debug("name: %s, bit: %d\n", name, (int)bit);
+#endif
+
+ cg = kzalloc(sizeof(*cg), GFP_KERNEL);
+ if (!cg)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = CLK_IGNORE_UNUSED;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.ops = &mtk_clk_gate_ops;
+
+ cg->set_addr = set_addr;
+ cg->clr_addr = clr_addr;
+ cg->sta_addr = sta_addr;
+ cg->bit = bit;
+ cg->flags = flags;
+
+ cg->hw.init = &init;
+
+ clk = clk_register(NULL, &cg->hw);
+ if (IS_ERR(clk))
+ kfree(cg);
+
+ return clk;
+}
diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
new file mode 100644
index 000000000..0d71aad56
--- /dev/null
+++ b/drivers/clk/mediatek/clk-gate.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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 __DRV_CLK_GATE_H
+#define __DRV_CLK_GATE_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+struct mtk_clk_gate {
+ struct clk_hw hw;
+ void __iomem *set_addr;
+ void __iomem *clr_addr;
+ void __iomem *sta_addr;
+ u8 bit;
+ u32 flags;
+};
+
+#define to_clk_gate(_hw) container_of(_hw, struct mtk_clk_gate, hw)
+
+#define CLK_GATE_INVERSE BIT(0)
+#define CLK_GATE_NO_SETCLR_REG BIT(1)
+
+struct clk *mtk_clk_register_gate(
+ const char *name,
+ const char *parent_name,
+ void __iomem *set_addr,
+ void __iomem *clr_addr,
+ void __iomem *sta_addr,
+ u8 bit,
+ u32 flags);
+
+#endif /* __DRV_CLK_GATE_H */
diff --git a/drivers/clk/mediatek/clk-mt6735-pg.c b/drivers/clk/mediatek/clk-mt6735-pg.c
new file mode 100644
index 000000000..dfdaee888
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-pg.c
@@ -0,0 +1,1251 @@
+/*
+* Copyright (c) 2014 MediaTek Inc.
+* Author: James Liao <jamesjj.liao@mediatek.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.
+*
+* 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/of.h>
+#include <linux/of_address.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-mt6735-pg.h"
+
+#include <dt-bindings/clock/mt6735-clk.h>
+
+#define VLTE_SUPPORT
+#ifdef VLTE_SUPPORT //Workaround is handled by ccci
+/* #include <mach/mt_gpio.h> */
+/* #include <mach/upmu_common.h> */
+#endif /* VLTE_SUPPORT */
+
+#ifdef Bring_Up
+#ifdef pr_debug
+#undef pr_debug
+#define pr_debug printk
+#endif
+#endif /* Bring_Up */
+
+#define CHECK_PWR_ST 0
+
+#ifndef GENMASK
+#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#endif
+
+#define clk_readl(addr) readl(addr)
+#define clk_writel(val, addr) \
+ do { writel(val, addr); wmb(); } while (0) /* sync_write */
+#define clk_setl(mask, addr) clk_writel(clk_readl(addr) | (mask), addr)
+#define clk_clrl(mask, addr) clk_writel(clk_readl(addr) & ~(mask), addr)
+
+#define mt_reg_sync_writel(v, a) \
+ do { \
+ __raw_writel((v), IOMEM((a))); \
+ dsb(); \
+ } while (0)
+#define spm_read(addr) __raw_readl(IOMEM(addr))
+#define spm_write(addr, val) mt_reg_sync_writel(val, addr)
+
+/*
+ * MTCMOS
+ */
+
+#define STA_POWER_DOWN 0
+#define STA_POWER_ON 1
+#define SUBSYS_PWR_DOWN 0
+#define SUBSYS_PWR_ON 1
+#define PWR_CLK_DIS (1U << 4)
+#define PWR_ON_2ND (1U << 3)
+#define PWR_ON (1U << 2)
+#define PWR_ISO (1U << 1)
+#define PWR_RST_B (1U << 0)
+
+struct subsys;
+
+struct subsys_ops {
+ int (*enable)(struct subsys *sys);
+ int (*disable)(struct subsys *sys);
+ int (*get_state)(struct subsys *sys);
+};
+
+struct subsys {
+ const char *name;
+ uint32_t sta_mask;
+ void __iomem *ctl_addr;
+ uint32_t sram_pdn_bits;
+ uint32_t sram_pdn_ack_bits;
+ uint32_t bus_prot_mask;
+ struct subsys_ops *ops;
+};
+
+static struct subsys_ops general_sys_ops;
+
+static void __iomem *infracfg_base;
+static void __iomem *spm_base;
+
+#define INFRACFG_REG(offset) (infracfg_base + offset)
+#define SPM_REG(offset) (spm_base + offset)
+
+/**************************************
+ * for non-CPU MTCMOS
+ **************************************/
+static DEFINE_SPINLOCK(spm_noncpu_lock);
+#define spm_mtcmos_noncpu_lock(flags) \
+do { \
+ spin_lock_irqsave(&spm_noncpu_lock, flags); \
+} while (0)
+
+#define spm_mtcmos_noncpu_unlock(flags) \
+do { \
+ spin_unlock_irqrestore(&spm_noncpu_lock, flags); \
+} while (0)
+
+/* FIXME: set correct value: S */
+#define SPM_PWR_STATUS SPM_REG(0x060c) /* correct */
+#define SPM_PWR_STATUS_2ND SPM_REG(0x0610) /* correct */
+
+#define SPM_MD_PWR_CON SPM_REG(0x0284) /* correct */
+#define SPM_C2K_PWR_CON SPM_REG(0x02d4) /* correct */
+#define SPM_CONN_PWR_CON SPM_REG(0x0280) /* correct */
+#define SPM_DIS_PWR_CON SPM_REG(0x023c)
+#define SPM_MFG_PWR_CON SPM_REG(0x0214)
+#define SPM_ISP_PWR_CON SPM_REG(0x0238)
+#define SPM_VDE_PWR_CON SPM_REG(0x0210)
+#define SPM_VEN_PWR_CON SPM_REG(0x0230)
+#define SPM_PCM_IM_PTR SPM_REG(0x0318) /* correct */
+#define SPM_PCM_IM_LEN SPM_REG(0x031c) /* correct */
+#define SPM_SLEEP_CPU_WAKEUP_EVENT SPM_REG(0x0814) /* correct */
+#define SPM_PCM_PASR_DPD_3 SPM_REG(0x0b6c) /* correct */
+
+#define INFRA_TOPAXI_PROTECTEN INFRACFG_REG(0x0220) /* correct */
+#define INFRA_TOPAXI_PROTECTSTA1 INFRACFG_REG(0x0228) /* correct */
+#define C2K_SPM_CTRL INFRACFG_REG(0x0338) /* correct */
+
+#define SPM_PROJECT_CODE 0xb16
+
+#define PWR_RST_B_BIT BIT(0)
+#define PWR_ISO_BIT BIT(1)
+#define PWR_ON_BIT BIT(2)
+#define PWR_ON_2ND_BIT BIT(3)
+#define PWR_CLK_DIS_BIT BIT(4)
+
+#define MD1_PWR_STA_MASK BIT(0) /* correct */
+#define MD2_PWR_STA_MASK BIT(22) /* correct */
+#define CONN_PWR_STA_MASK BIT(1) /* correct */
+#define DIS_PWR_STA_MASK BIT(3)
+#define MFG_PWR_STA_MASK BIT(4)
+#define ISP_PWR_STA_MASK BIT(5)
+#define VDE_PWR_STA_MASK BIT(7)
+#define VEN_PWR_STA_MASK BIT(21)
+
+// correct: S
+#define SRAM_PDN (0xf << 8) //VDEC, VENC, ISP, DISP
+#define MFG_SRAM_PDN (0xf << 8)
+#define MD_SRAM_PDN (0x1 << 8) //MD1, C2K
+#define CONN_SRAM_PDN (0x1 << 8)
+// correct: E
+
+#define VDE_SRAM_ACK (0x1 << 12)
+#define VEN_SRAM_ACK (0xf << 12)
+#define ISP_SRAM_ACK (0x3 << 12)
+#define DIS_SRAM_ACK (0x1 << 12)
+#define MFG_SRAM_ACK (0x1 << 12)
+
+#define DISP_PROT_MASK (BIT(1) | BIT(2))
+#define MFG_2D_PROT_MASK (BIT(14) | GENMASK(23, 21))
+/* FIXME: set correct value: E */
+#define MD1_PROT_MASK ((0x1<<24) | (0x1<<25) | (0x1<<26) | (0x1<<27) | \
+ (0x1<<28)) //bit 24,25,26,27,28
+#define MD2_PROT_MASK ((0x1<<29) | (0x1<<30) | (0x1<<31)) //bit 29, 30, 31
+#define CONN_PROT_MASK ((0x1<<2) | (0x1<<8)) //bit 2, 8
+
+#if defined(CONFIG_ARCH_MT6735M)
+//#define MD_PWRON_BY_CPU
+#elif defined(CONFIG_ARCH_MT6753)
+#define MD_PWRON_BY_CPU
+#else
+#define MD_PWRON_BY_CPU
+#endif
+
+static struct subsys syss[] = /* NR_SYSS */ /* FIXME: set correct value */
+{
+ [SYS_MD1] = {
+ .name = __stringify(SYS_MD1),
+ .sta_mask = MD1_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_MD_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12), /* GENMASK(15, 12), */
+ .bus_prot_mask = 0,
+ .ops = &general_sys_ops,
+ },
+ [SYS_MD2] = {
+ .name = __stringify(SYS_MD2),
+ .sta_mask = MD2_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_C2K_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12), /* GENMASK(15, 12), */
+ .bus_prot_mask = 0,
+ .ops = &general_sys_ops,
+ },
+ [SYS_CONN] = {
+ .name = __stringify(SYS_CONN),
+ .sta_mask = CONN_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_CONN_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12), /* GENMASK(15, 12), */
+ .bus_prot_mask = 0,
+ .ops = &general_sys_ops,
+ },
+ [SYS_DIS] = {
+ .name = __stringify(SYS_DIS),
+ .sta_mask = DIS_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_DIS_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12), /* GENMASK(15, 12), */
+ .bus_prot_mask = DISP_PROT_MASK,
+ .ops = &general_sys_ops,
+ },
+ [SYS_MFG] = {
+ .name = __stringify(SYS_MFG),
+ .sta_mask = MFG_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_MFG_PWR_CON, */
+ .sram_pdn_bits = GENMASK(13, 8),
+ .sram_pdn_ack_bits = GENMASK(21, 16),
+ .bus_prot_mask = MFG_2D_PROT_MASK,
+ .ops = &general_sys_ops,
+ },
+ [SYS_ISP] = {
+ .name = __stringify(SYS_ISP),
+ .sta_mask = ISP_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_ISP_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(13, 12), /* GENMASK(15, 12), */
+ .bus_prot_mask = 0,
+ .ops = &general_sys_ops,
+ },
+ [SYS_VDE] = {
+ .name = __stringify(SYS_VDE),
+ .sta_mask = VDE_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_VDE_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12), /* GENMASK(15, 12), */
+ .bus_prot_mask = 0,
+ .ops = &general_sys_ops,
+ },
+ [SYS_VEN] = {
+ .name = __stringify(SYS_VEN),
+ .sta_mask = VEN_PWR_STA_MASK,
+ .ctl_addr = NULL, /* SPM_VEN_PWR_CON, */
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(15, 12),
+ .bus_prot_mask = 0,
+ .ops = &general_sys_ops,
+ },
+};
+
+static struct pg_callbacks *g_pgcb;
+
+struct pg_callbacks *register_pg_callback(struct pg_callbacks *pgcb)
+{
+ struct pg_callbacks *old_pgcb = g_pgcb;
+
+ g_pgcb = pgcb;
+ return old_pgcb;
+}
+
+static struct subsys *id_to_sys(unsigned int id)
+{
+ return id < NR_SYSS ? &syss[id] : NULL;
+}
+
+/** sync from mt_spm_mtcmos.c for bring up */
+static int spm_mtcmos_ctrl_connsys(int state)
+{
+ int err = 0;
+ volatile unsigned int val;
+ unsigned long flags;
+ int count = 0;
+
+ pr_debug("[CCF] %s: state=%d: S\n", __func__, state);
+ spm_mtcmos_noncpu_lock(flags);
+
+ if (state == STA_POWER_DOWN) {
+ spm_write(INFRA_TOPAXI_PROTECTEN,
+ spm_read(INFRA_TOPAXI_PROTECTEN) | CONN_PROT_MASK);
+ while ((spm_read(INFRA_TOPAXI_PROTECTSTA1) & CONN_PROT_MASK)
+ != CONN_PROT_MASK) {
+ count++;
+ if(count>1000)
+ break;
+ }
+
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) | CONN_SRAM_PDN);
+
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) | PWR_ISO);
+
+ val = spm_read(SPM_CONN_PWR_CON);
+ val = (val & ~PWR_RST_B) | PWR_CLK_DIS;
+ spm_write(SPM_CONN_PWR_CON, val);
+
+ spm_write(SPM_CONN_PWR_CON,
+ spm_read(SPM_CONN_PWR_CON) & ~(PWR_ON | PWR_ON_2ND));
+
+ while ((spm_read(SPM_PWR_STATUS) & CONN_PWR_STA_MASK)
+ || (spm_read(SPM_PWR_STATUS_2ND) & CONN_PWR_STA_MASK)) {
+ }
+ } else {
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) | PWR_ON);
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) | PWR_ON_2ND);
+
+ while (!(spm_read(SPM_PWR_STATUS) & CONN_PWR_STA_MASK)
+ || !(spm_read(SPM_PWR_STATUS_2ND) & CONN_PWR_STA_MASK)) {
+ }
+
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) & ~PWR_CLK_DIS);
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) & ~PWR_ISO);
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) | PWR_RST_B);
+
+ spm_write(SPM_CONN_PWR_CON, spm_read(SPM_CONN_PWR_CON) & ~CONN_SRAM_PDN);
+
+ spm_write(INFRA_TOPAXI_PROTECTEN,
+ spm_read(INFRA_TOPAXI_PROTECTEN) & ~CONN_PROT_MASK);
+ while (spm_read(INFRA_TOPAXI_PROTECTSTA1) & CONN_PROT_MASK) {
+ }
+ }
+
+ spm_mtcmos_noncpu_unlock(flags);
+
+ return err;
+}
+
+static int spm_mtcmos_ctrl_mdsys1(int state)
+{
+ int err = 0;
+ volatile unsigned int val;
+ unsigned long flags;
+ int count = 0;
+
+ pr_debug("[CCF] %s: state=%d: S\n", __func__, state);
+ spm_mtcmos_noncpu_lock(flags);
+
+ if (state == STA_POWER_DOWN) {
+ spm_write(INFRA_TOPAXI_PROTECTEN,
+ spm_read(INFRA_TOPAXI_PROTECTEN) | MD1_PROT_MASK);
+ while ((spm_read(INFRA_TOPAXI_PROTECTSTA1) & MD1_PROT_MASK)
+ != MD1_PROT_MASK) {
+ count++;
+ if(count>1000)
+ break;
+ }
+
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) | MD_SRAM_PDN);
+
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) | PWR_ISO);
+
+#ifdef VLTE_SUPPORT
+ //enable LTE LS ISO
+ val = spm_read(C2K_SPM_CTRL);
+ val |= 0x40;
+ spm_write(C2K_SPM_CTRL, val);
+#endif /* VLTE_SUPPORT */
+
+ val = spm_read(SPM_MD_PWR_CON);
+ val = (val & ~PWR_RST_B) | PWR_CLK_DIS;
+ spm_write(SPM_MD_PWR_CON, val);
+
+ spm_write(SPM_MD_PWR_CON,
+ spm_read(SPM_MD_PWR_CON) & ~(PWR_ON | PWR_ON_2ND));
+
+ while ((spm_read(SPM_PWR_STATUS) & MD1_PWR_STA_MASK)
+ || (spm_read(SPM_PWR_STATUS_2ND) & MD1_PWR_STA_MASK)) {
+ }
+
+ } else { /* STA_POWER_ON */
+
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) | PWR_ON);
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) | PWR_ON_2ND);
+
+ while (!(spm_read(SPM_PWR_STATUS) & MD1_PWR_STA_MASK)
+ || !(spm_read(SPM_PWR_STATUS_2ND) & MD1_PWR_STA_MASK)) {
+ }
+
+#ifdef MD_PWRON_BY_CPU
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) & ~PWR_CLK_DIS);
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) & ~PWR_ISO);
+
+ //disable LTE LS ISO
+ val = spm_read(C2K_SPM_CTRL);
+ val &= ~(0x40);
+ spm_write(C2K_SPM_CTRL, val);
+
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) | PWR_RST_B);
+#else
+ printk("MD power on by SPM\n");
+ spm_write(SPM_PCM_PASR_DPD_3, 0xbeef);
+ spm_write(SPM_SLEEP_CPU_WAKEUP_EVENT, 0x1);
+ while (spm_read(SPM_PCM_PASR_DPD_3)) {
+ count++;
+ udelay(1);
+ if(count>1000) {
+ printk("MD power on: SPM no response\n");
+ printk("PCM_IM_PTR : 0x%x (%u) \n", spm_read(SPM_PCM_IM_PTR),
+ spm_read(SPM_PCM_IM_LEN));
+ BUG();
+ }
+ }
+#endif
+
+ spm_write(SPM_MD_PWR_CON, spm_read(SPM_MD_PWR_CON) & ~MD_SRAM_PDN);
+
+ spm_write(INFRA_TOPAXI_PROTECTEN,
+ spm_read(INFRA_TOPAXI_PROTECTEN) & ~MD1_PROT_MASK);
+ while (spm_read(INFRA_TOPAXI_PROTECTSTA1) & MD1_PROT_MASK) {
+ }
+ }
+
+ spm_mtcmos_noncpu_unlock(flags);
+
+ return err;
+}
+
+static int spm_mtcmos_ctrl_mdsys2(int state)
+{
+ int err = 0;
+ volatile unsigned int val;
+ unsigned long flags;
+ int count = 0;
+
+ pr_debug("[CCF] %s: state=%d: S\n", __func__, state);
+ spm_mtcmos_noncpu_lock(flags);
+
+ if (state == STA_POWER_DOWN) {
+ spm_write(INFRA_TOPAXI_PROTECTEN,
+ spm_read(INFRA_TOPAXI_PROTECTEN) | MD2_PROT_MASK);
+ while ((spm_read(INFRA_TOPAXI_PROTECTSTA1) & MD2_PROT_MASK)
+ != MD2_PROT_MASK) {
+ count++;
+ if(count>1000)
+ break;
+ }
+
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) | MD_SRAM_PDN);
+
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) | PWR_ISO);
+
+ val = spm_read(SPM_C2K_PWR_CON);
+ val = (val & ~PWR_RST_B) | PWR_CLK_DIS;
+ spm_write(SPM_C2K_PWR_CON, val);
+
+ spm_write(SPM_C2K_PWR_CON,
+ spm_read(SPM_C2K_PWR_CON) & ~(PWR_ON | PWR_ON_2ND));
+
+ while ((spm_read(SPM_PWR_STATUS) & MD2_PWR_STA_MASK)
+ || (spm_read(SPM_PWR_STATUS_2ND) & MD2_PWR_STA_MASK)) {
+ }
+
+ } else { /* STA_POWER_ON */
+
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) | PWR_ON);
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) | PWR_ON_2ND);
+
+ while (!(spm_read(SPM_PWR_STATUS) & MD2_PWR_STA_MASK)
+ || !(spm_read(SPM_PWR_STATUS_2ND) & MD2_PWR_STA_MASK)) {
+ }
+
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) & ~PWR_CLK_DIS);
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) & ~PWR_ISO);
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) | PWR_RST_B);
+
+ spm_write(SPM_C2K_PWR_CON, spm_read(SPM_C2K_PWR_CON) & ~MD_SRAM_PDN);
+
+ spm_write(INFRA_TOPAXI_PROTECTEN,
+ spm_read(INFRA_TOPAXI_PROTECTEN) & ~MD2_PROT_MASK);
+ while (spm_read(INFRA_TOPAXI_PROTECTSTA1) & MD2_PROT_MASK) {
+ }
+ }
+
+ spm_mtcmos_noncpu_unlock(flags);
+
+ return err;
+}
+
+static void set_bus_protect(int en, uint32_t mask, unsigned long expired)
+{
+ if (!mask)
+ return;
+
+ if (en) {
+ clk_setl(mask, INFRA_TOPAXI_PROTECTEN);
+
+#if !DUMMY_REG_TEST
+ while ((clk_readl(INFRA_TOPAXI_PROTECTSTA1) & mask) != mask) {
+ if (time_after(jiffies, expired)) {
+ WARN_ON(1);
+ break;
+ }
+ }
+#endif /* !DUMMY_REG_TEST */
+ } else {
+ clk_clrl(mask, INFRA_TOPAXI_PROTECTEN);
+
+#if !DUMMY_REG_TEST
+ while (clk_readl(INFRA_TOPAXI_PROTECTSTA1) & mask) {
+ if (time_after(jiffies, expired)) {
+ WARN_ON(1);
+ break;
+ }
+ }
+#endif /* !DUMMY_REG_TEST */
+ }
+}
+
+static int spm_mtcmos_power_off_general_locked(struct subsys *sys,
+ int wait_power_ack, int ext_pwr_delay)
+{
+ unsigned long expired = jiffies + HZ / 10;
+ void __iomem *ctl_addr = sys->ctl_addr;
+#if !DUMMY_REG_TEST
+ uint32_t sram_pdn_ack = sys->sram_pdn_ack_bits;
+#endif /* !DUMMY_REG_TEST */
+
+ pr_debug("sys: %s\n", sys->name);
+
+ /* BUS_PROTECT */
+ set_bus_protect(1, sys->bus_prot_mask, expired);
+
+ /* SRAM_PDN */
+ clk_setl(sys->sram_pdn_bits, ctl_addr);
+
+ /* wait until SRAM_PDN_ACK all 1 */
+#if !DUMMY_REG_TEST
+ while (((clk_readl(ctl_addr) & sram_pdn_ack) != sram_pdn_ack)) {
+ if (time_after(jiffies, expired)) {
+ WARN_ON(1);
+ break;
+ }
+ }
+#endif /* !DUMMY_REG_TEST */
+
+ clk_setl(PWR_ISO_BIT, ctl_addr);
+ clk_clrl(PWR_RST_B_BIT, ctl_addr);
+ clk_setl(PWR_CLK_DIS_BIT, ctl_addr);
+
+ clk_clrl(PWR_ON_BIT, ctl_addr);
+ clk_clrl(PWR_ON_2ND_BIT, ctl_addr);
+
+ /* extra delay after power off */
+ if (ext_pwr_delay > 0)
+ udelay(ext_pwr_delay);
+
+ if (wait_power_ack) {
+ /* wait until PWR_ACK = 0 */
+#if !DUMMY_REG_TEST
+ while ((clk_readl(SPM_PWR_STATUS) & sys->sta_mask)
+ || (clk_readl(SPM_PWR_STATUS_2ND) & sys->sta_mask)) {
+ if (time_after(jiffies, expired)) {
+ WARN_ON(1);
+ break;
+ }
+ }
+#endif /* !DUMMY_REG_TEST */
+ }
+
+ return 0;
+}
+
+static int spm_mtcmos_power_on_general_locked(
+ struct subsys *sys, int wait_power_ack, int ext_pwr_delay)
+{
+ unsigned long expired = jiffies + HZ / 10;
+ void __iomem *ctl_addr = sys->ctl_addr;
+#if !DUMMY_REG_TEST
+ uint32_t sram_pdn_ack = sys->sram_pdn_ack_bits;
+#endif /* !DUMMY_REG_TEST */
+
+ pr_debug("sys: %s\n", sys->name);
+
+ clk_setl(PWR_ON_BIT, ctl_addr);
+ clk_setl(PWR_ON_2ND_BIT, ctl_addr);
+
+ /* extra delay after power on */
+ if (ext_pwr_delay > 0)
+ udelay(ext_pwr_delay);
+
+ if (wait_power_ack) {
+ /* wait until PWR_ACK = 1 */
+#if !DUMMY_REG_TEST
+ while (!(clk_readl(SPM_PWR_STATUS) & sys->sta_mask)
+ || !(clk_readl(SPM_PWR_STATUS_2ND) & sys->sta_mask)) {
+ if (time_after(jiffies, expired)) {
+ WARN_ON(1);
+ break;
+ }
+ }
+#endif /* !DUMMY_REG_TEST */
+ }
+
+ clk_clrl(PWR_CLK_DIS_BIT, ctl_addr);
+ clk_clrl(PWR_ISO_BIT, ctl_addr);
+ clk_setl(PWR_RST_B_BIT, ctl_addr);
+
+ /* SRAM_PDN */
+ clk_clrl(sys->sram_pdn_bits, ctl_addr);
+
+ /* wait until SRAM_PDN_ACK all 0 */
+#if !DUMMY_REG_TEST
+ while (sram_pdn_ack && (clk_readl(ctl_addr) & sram_pdn_ack)) {
+ if (time_after(jiffies, expired)) {
+ WARN_ON(1);
+ break;
+ }
+ }
+#endif /* !DUMMY_REG_TEST */
+
+ /* BUS_PROTECT */
+ set_bus_protect(0, sys->bus_prot_mask, expired);
+
+ return 0;
+}
+
+static int general_sys_enable_op(struct subsys *sys)
+{
+ return spm_mtcmos_power_on_general_locked(sys, 1, 0);
+}
+
+static int general_sys_disable_op(struct subsys *sys)
+{
+ return spm_mtcmos_power_off_general_locked(sys, 1, 0);
+}
+
+static int sys_get_state_op(struct subsys *sys)
+{
+ unsigned int sta = clk_readl(SPM_PWR_STATUS);
+ unsigned int sta_s = clk_readl(SPM_PWR_STATUS_2ND);
+
+ return (sta & sys->sta_mask) && (sta_s & sys->sta_mask);
+}
+
+static struct subsys_ops general_sys_ops = {
+ .enable = general_sys_enable_op,
+ .disable = general_sys_disable_op,
+ .get_state = sys_get_state_op,
+};
+
+static int subsys_is_on(enum subsys_id id)
+{
+ int r;
+ struct subsys *sys = id_to_sys(id);
+
+ BUG_ON(!sys);
+
+ r = sys->ops->get_state(sys);
+
+ pr_debug("%d: sys: %s\n", r, sys->name);
+
+ return r;
+}
+
+static int enable_subsys(enum subsys_id id)
+{
+ int r;
+ unsigned long flags;
+ struct subsys *sys = id_to_sys(id);
+
+ BUG_ON(!sys);
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: sys=%s, id=%d\n", __func__, sys->name, id);
+ switch (id) {
+ case SYS_MD1:
+#ifdef VLTE_SUPPORT //Workaround is handled by ccci
+ /* mt_set_gpio_out(GPIO_LTE_VSRAM_EXT_POWER_EN_PIN,1);
+ pmic_config_interface(0x04D6, 0x1, 0x1, 0);
+ udelay(200); */
+#endif /* VLTE_SUPPORT */
+ spm_mtcmos_ctrl_mdsys1(STA_POWER_ON);
+ break;
+ case SYS_MD2:
+ spm_mtcmos_ctrl_mdsys2(STA_POWER_ON);
+ break;
+ case SYS_CONN:
+ spm_mtcmos_ctrl_connsys(STA_POWER_ON);
+ break;
+ default:
+ break;
+ }
+ return 0;
+#endif
+
+ mtk_clk_lock(flags);
+
+#if CHECK_PWR_ST
+ if (sys->ops->get_state(sys) == SUBSYS_PWR_ON) {
+ mtk_clk_unlock(flags);
+ return 0;
+ }
+#endif /* CHECK_PWR_ST */
+
+ r = sys->ops->enable(sys);
+ WARN_ON(r);
+
+ mtk_clk_unlock(flags);
+
+ if (g_pgcb && g_pgcb->after_on)
+ g_pgcb->after_on(id);
+
+ return r;
+}
+
+static int disable_subsys(enum subsys_id id)
+{
+ int r;
+ unsigned long flags;
+ struct subsys *sys = id_to_sys(id);
+
+ BUG_ON(!sys);
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: sys=%s, id=%d\n", __func__, sys->name, id);
+ switch (id) {
+ case SYS_MD1:
+ spm_mtcmos_ctrl_mdsys1(STA_POWER_DOWN);
+#ifdef VLTE_SUPPORT //Workaround is handled by ccci
+ /* pmic_config_interface(0x04D6, 0x0, 0x1, 0); //bit[0] =>1'b0
+ mt_set_gpio_out(GPIO_LTE_VSRAM_EXT_POWER_EN_PIN,0); */
+#endif /* VLTE_SUPPORT */
+ break;
+ case SYS_MD2:
+ spm_mtcmos_ctrl_mdsys2(STA_POWER_DOWN);
+ break;
+ case SYS_CONN:
+ spm_mtcmos_ctrl_connsys(STA_POWER_DOWN);
+ break;
+ default:
+ break;
+ }
+ return 0;
+#endif
+
+ /* TODO: check all clocks related to this subsys are off */
+ /* could be power off or not */
+
+ if (g_pgcb && g_pgcb->before_off)
+ g_pgcb->before_off(id);
+
+ mtk_clk_lock(flags);
+
+#if CHECK_PWR_ST
+ if (sys->ops->get_state(sys) == SUBSYS_PWR_DOWN) {
+ mtk_clk_unlock(flags);
+ return 0;
+ }
+#endif /* CHECK_PWR_ST */
+
+ r = sys->ops->disable(sys);
+ WARN_ON(r);
+
+ mtk_clk_unlock(flags);
+
+ return r;
+}
+
+/*
+ * power_gate
+ */
+
+struct mt_power_gate {
+ struct clk_hw hw;
+ struct clk *pre_clk;
+ enum subsys_id pd_id;
+};
+
+#define to_power_gate(_hw) container_of(_hw, struct mt_power_gate, hw)
+
+static int pg_enable(struct clk_hw *hw)
+{
+ struct mt_power_gate *pg = to_power_gate(hw);
+
+ pr_debug("%s, id: %u\n", __clk_get_name(hw->clk), pg->pd_id);
+
+ return enable_subsys(pg->pd_id);
+}
+
+static void pg_disable(struct clk_hw *hw)
+{
+ struct mt_power_gate *pg = to_power_gate(hw);
+
+ pr_debug("%s, id: %u\n", __clk_get_name(hw->clk), pg->pd_id);
+
+ disable_subsys(pg->pd_id);
+}
+
+static int pg_is_enabled(struct clk_hw *hw)
+{
+ struct mt_power_gate *pg = to_power_gate(hw);
+#ifdef Bring_Up
+ return 1;
+#endif
+
+ return subsys_is_on(pg->pd_id);
+}
+
+int pg_prepare(struct clk_hw *hw)
+{
+/*#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ spm_mtcmos_ctrl_connsys(STA_POWER_ON);
+ return 0;
+#endif*/
+ int r;
+ struct mt_power_gate *pg = to_power_gate(hw);
+
+ pr_debug("%s, pre_clk: %s\n", __clk_get_name(hw->clk),
+ pg->pre_clk ? __clk_get_name(pg->pre_clk) : "");
+
+ if (pg->pre_clk) {
+ r = clk_prepare_enable(pg->pre_clk);
+ if (r)
+ return r;
+ }
+
+ return pg_enable(hw);
+
+}
+
+void pg_unprepare(struct clk_hw *hw)
+{
+/*#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ spm_mtcmos_ctrl_connsys(STA_POWER_DOWN);
+ return;
+#endif*/
+ struct mt_power_gate *pg = to_power_gate(hw);
+
+ pr_debug("%s, pre_clk: %s\n", __clk_get_name(hw->clk),
+ pg->pre_clk ? __clk_get_name(pg->pre_clk) : "");
+
+ pg_disable(hw);
+
+ if (pg->pre_clk)
+ clk_disable_unprepare(pg->pre_clk);
+}
+
+static const struct clk_ops mt_power_gate_ops = {
+ .prepare = pg_prepare,
+ .unprepare = pg_unprepare,
+ .is_enabled = pg_is_enabled,
+};
+
+struct clk *mt_clk_register_power_gate(
+ const char *name,
+ const char *parent_name,
+ struct clk *pre_clk,
+ enum subsys_id pd_id)
+{
+ struct mt_power_gate *pg;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pg = kzalloc(sizeof(*pg), GFP_KERNEL);
+ if (!pg)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = CLK_IGNORE_UNUSED;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.ops = &mt_power_gate_ops;
+
+ pg->pre_clk = pre_clk;
+ pg->pd_id = pd_id;
+ pg->hw.init = &init;
+
+ clk = clk_register(NULL, &pg->hw);
+ if (IS_ERR(clk))
+ kfree(pg);
+
+ return clk;
+}
+
+#define pg_md1 "pg_md1"
+#define pg_md2 "pg_md2"
+#define pg_conn "pg_conn"
+#define pg_dis "pg_dis"
+#define pg_mfg "pg_mfg"
+#define pg_isp "pg_isp"
+#define pg_vde "pg_vde"
+#define pg_ven "pg_ven"
+#define pg_mfg_2d "pg_mfg_2d"
+
+/* FIXME: set correct value: S */
+#define md_sel "md_sel"
+#define conn_sel "conn_sel"
+#define mm_sel "mm_sel"
+#define vdec_sel "vdec_sel"
+#define venc_sel "venc_sel"
+#define mfg_sel "mfg_sel"
+/* FIXME: set correct value: E */
+
+struct mtk_power_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ const char *pre_clk_name;
+ enum subsys_id pd_id;
+};
+
+#define PGATE(_id, _name, _parent, _pre_clk, _pd_id) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .pre_clk_name = _pre_clk, \
+ .pd_id = _pd_id, \
+ }
+
+/* FIXME: all values needed to be verified */
+struct mtk_power_gate scp_clks[] __initdata = {
+ PGATE(SCP_SYS_MD1, pg_md1, NULL, NULL, SYS_MD1), //md_sel
+ PGATE(SCP_SYS_MD2, pg_md2, NULL, NULL, SYS_MD2), //md_sel
+ PGATE(SCP_SYS_CONN, pg_conn, NULL, NULL, SYS_CONN), //conn_sel
+ PGATE(SCP_SYS_DIS, pg_dis, NULL, NULL, SYS_DIS), //mm_sel
+ PGATE(SCP_SYS_MFG, pg_mfg, pg_mfg_2d, NULL, SYS_MFG), //mfg_sel
+ PGATE(SCP_SYS_ISP, pg_isp, NULL, NULL, SYS_ISP), //pre_clk null?
+ PGATE(SCP_SYS_VDE, pg_vde, NULL, NULL, SYS_VDE), //vdec_sel
+ PGATE(SCP_SYS_VEN, pg_ven, NULL, NULL, SYS_VEN), //venc_sel
+};
+
+static void __init init_clk_scpsys(
+ void __iomem *infracfg_reg,
+ void __iomem *spm_reg ,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+ struct clk *pre_clk;
+
+ infracfg_base = infracfg_reg;
+ spm_base = spm_reg;
+
+ syss[SYS_MD1].ctl_addr = SPM_MD_PWR_CON;
+ syss[SYS_MD2].ctl_addr = SPM_C2K_PWR_CON;
+ syss[SYS_CONN].ctl_addr = SPM_CONN_PWR_CON;
+ syss[SYS_DIS].ctl_addr = SPM_DIS_PWR_CON;
+ syss[SYS_MFG].ctl_addr = SPM_MFG_PWR_CON;
+ syss[SYS_ISP].ctl_addr = SPM_ISP_PWR_CON;
+ syss[SYS_VDE].ctl_addr = SPM_VDE_PWR_CON;
+ syss[SYS_VEN].ctl_addr = SPM_VEN_PWR_CON;
+
+ for (i = 0; i < ARRAY_SIZE(scp_clks); i++) {
+ struct mtk_power_gate *pg = &scp_clks[i];
+
+ pre_clk = pg->pre_clk_name ?
+ __clk_lookup(pg->pre_clk_name) : NULL;
+
+ clk = mt_clk_register_power_gate(pg->name, pg->parent_name,
+ pre_clk, pg->pd_id);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ pg->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[pg->id] = clk;
+
+ pr_debug("pgate %3d: %s\n", i, pg->name);
+ }
+}
+
+/*
+ * device tree support
+ */
+
+/* TODO: remove this function */
+static struct clk_onecell_data *alloc_clk_data(unsigned int clk_num)
+{
+ int i;
+ struct clk_onecell_data *clk_data;
+
+ clk_data = kzalloc(sizeof(clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return NULL;
+
+ clk_data->clks = kcalloc(clk_num, sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return NULL;
+ }
+
+ clk_data->clk_num = clk_num;
+
+ for (i = 0; i < clk_num; ++i)
+ clk_data->clks[i] = ERR_PTR(-ENOENT);
+
+ return clk_data;
+}
+
+/* TODO: remove this function */
+static void __iomem *get_reg(struct device_node *np, int index)
+{
+#if DUMMY_REG_TEST
+ return kzalloc(PAGE_SIZE, GFP_KERNEL);
+#else
+ return of_iomap(np, index);
+#endif
+}
+
+static void __init mt_scpsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *infracfg_reg;
+ void __iomem *spm_reg;
+ int r;
+
+ infracfg_reg = get_reg(node, 0);
+ spm_reg = get_reg(node, 1);
+
+ if (!infracfg_reg || !spm_reg) {
+ pr_err("clk-pg-mt6735: missing reg\n");
+ return;
+ }
+
+ pr_debug("sys: %s, reg: 0x%p, 0x%p\n",
+ node->name, infracfg_reg, spm_reg);
+
+ clk_data = alloc_clk_data(SCP_NR_SYSS);
+
+ init_clk_scpsys(infracfg_reg, spm_reg, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_pg_regs, "mediatek,mt6735-scpsys", mt_scpsys_init);
+
+#if CLK_DEBUG
+
+/*
+ * debug / unit test
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+static char last_cmd[128] = "null";
+
+static int test_pg_dump_regs(struct seq_file *s, void *v)
+{
+ int i;
+
+ for (i = 0; i < NR_SYSS; i++) {
+ if (!syss[i].ctl_addr)
+ continue;
+
+ seq_printf(s, "%10s: [0x%p]: 0x%08x\n", syss[i].name,
+ syss[i].ctl_addr, clk_readl(syss[i].ctl_addr));
+ }
+
+ return 0;
+}
+
+static void dump_pg_state(const char *clkname, struct seq_file *s)
+{
+ struct clk *c = __clk_lookup(clkname);
+ struct clk *p = IS_ERR_OR_NULL(c) ? NULL : __clk_get_parent(c);
+
+ if (IS_ERR_OR_NULL(c)) {
+ seq_printf(s, "[%17s: NULL]\n", clkname);
+ return;
+ }
+
+ seq_printf(s, "[%17s: %3s, %3d, %3d, %10ld, %17s]\n",
+ __clk_get_name(c),
+ __clk_is_enabled(c) ? "ON" : "off",
+ __clk_get_prepare_count(c),
+ __clk_get_enable_count(c),
+ __clk_get_rate(c),
+ p ? __clk_get_name(p) : "");
+
+ clk_put(c);
+}
+
+static int test_pg_dump_state_all(struct seq_file *s, void *v)
+{
+ static const char * const clks[] = {
+ pg_md1,
+ pg_md2,
+ pg_conn,
+ pg_dis,
+ pg_mfg,
+ pg_isp,
+ pg_vde,
+ pg_ven,
+ };
+
+ int i;
+
+ pr_debug("\n");
+ for (i = 0; i < ARRAY_SIZE(clks); i++)
+ dump_pg_state(clks[i], s);
+
+ return 0;
+}
+
+static struct {
+ const char *name;
+ struct clk *clk;
+} g_clks[] = {
+ {.name = pg_md1},
+ {.name = pg_vde},
+ {.name = pg_ven},
+ {.name = pg_mfg},
+};
+
+static int test_pg_1(struct seq_file *s, void *v)
+{
+ int i;
+
+ pr_debug("\n");
+
+ for (i = 0; i < ARRAY_SIZE(g_clks); i++) {
+ g_clks[i].clk = __clk_lookup(g_clks[i].name);
+ if (IS_ERR_OR_NULL(g_clks[i].clk)) {
+ seq_printf(s, "clk_get(%s): NULL\n",
+ g_clks[i].name);
+ continue;
+ }
+
+ clk_prepare_enable(g_clks[i].clk);
+ seq_printf(s, "clk_prepare_enable(%s)\n",
+ __clk_get_name(g_clks[i].clk));
+ }
+
+ return 0;
+}
+
+static int test_pg_2(struct seq_file *s, void *v)
+{
+ int i;
+
+ pr_debug("\n");
+
+ for (i = 0; i < ARRAY_SIZE(g_clks); i++) {
+ if (IS_ERR_OR_NULL(g_clks[i].clk)) {
+ seq_printf(s, "(%s).clk: NULL\n",
+ g_clks[i].name);
+ continue;
+ }
+
+ seq_printf(s, "clk_disable_unprepare(%s)\n",
+ __clk_get_name(g_clks[i].clk));
+ clk_disable_unprepare(g_clks[i].clk);
+ clk_put(g_clks[i].clk);
+ }
+
+ return 0;
+}
+
+static int test_pg_show(struct seq_file *s, void *v)
+{
+ static const struct {
+ int (*fn)(struct seq_file *, void *);
+ const char *cmd;
+ } cmds[] = {
+ {.cmd = "dump_regs", .fn = test_pg_dump_regs},
+ {.cmd = "dump_state", .fn = test_pg_dump_state_all},
+ {.cmd = "1", .fn = test_pg_1},
+ {.cmd = "2", .fn = test_pg_2},
+ };
+
+ int i;
+
+ pr_debug("last_cmd: %s\n", last_cmd);
+
+ for (i = 0; i < ARRAY_SIZE(cmds); i++) {
+ if (strcmp(cmds[i].cmd, last_cmd) == 0)
+ return cmds[i].fn(s, v);
+ }
+
+ return 0;
+}
+
+static int test_pg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, test_pg_show, NULL);
+}
+
+static ssize_t test_pg_write(
+ struct file *file,
+ const char __user *buffer,
+ size_t count,
+ loff_t *data)
+{
+ char desc[sizeof(last_cmd)];
+ int len = 0;
+
+ pr_debug("count: %zu\n", count);
+ len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
+ if (copy_from_user(desc, buffer, len))
+ return 0;
+
+ desc[len] = '\0';
+ strcpy(last_cmd, desc);
+ if (last_cmd[len - 1] == '\n')
+ last_cmd[len - 1] = 0;
+
+ return count;
+}
+
+static const struct file_operations test_pg_fops = {
+ .owner = THIS_MODULE,
+ .open = test_pg_open,
+ .read = seq_read,
+ .write = test_pg_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init debug_init(void)
+{
+ static int init;
+ struct proc_dir_entry *entry;
+
+ pr_debug("init: %d\n", init);
+
+ if (init)
+ return 0;
+
+ ++init;
+
+ entry = proc_create("test_pg", 0, 0, &test_pg_fops);
+ if (!entry)
+ return -ENOMEM;
+
+ ++init;
+ return 0;
+}
+
+static void __exit debug_exit(void)
+{
+ remove_proc_entry("test_pg", NULL);
+}
+
+module_init(debug_init);
+module_exit(debug_exit);
+
+#endif /* CLK_DEBUG */
diff --git a/drivers/clk/mediatek/clk-mt6735-pg.h b/drivers/clk/mediatek/clk-mt6735-pg.h
new file mode 100644
index 000000000..f37546d80
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-pg.h
@@ -0,0 +1,24 @@
+#ifndef __DRV_CLK_MT6735_PG_H
+#define __DRV_CLK_MT6735_PG_H
+
+enum subsys_id {
+ SYS_MD1 = 0,
+ SYS_MD2 = 1,
+ SYS_CONN = 2,
+ SYS_DIS = 3,
+ SYS_MFG = 4,
+ SYS_ISP = 5,
+ SYS_VDE = 6,
+ SYS_VEN = 7,
+ NR_SYSS = 8,
+};
+
+struct pg_callbacks {
+ void (*before_off)(enum subsys_id sys);
+ void (*after_on)(enum subsys_id sys);
+};
+
+/* register new pg_callbacks and return previous pg_callbacks. */
+extern struct pg_callbacks *register_pg_callback(struct pg_callbacks *pgcb);
+
+#endif /* __DRV_CLK_MT6735_PG_H */
diff --git a/drivers/clk/mediatek/clk-mt6735-pll.c b/drivers/clk/mediatek/clk-mt6735-pll.c
new file mode 100644
index 000000000..6de07e47e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-pll.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-mt6735-pll.h"
+
+#ifdef Bring_Up
+#ifdef pr_debug
+#undef pr_debug
+#define pr_debug printk
+#endif
+#endif /* Bring_Up */
+
+#ifndef GENMASK
+#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#endif
+
+/*
+ * clk_pll
+ */
+
+#define PLL_BASE_EN BIT(0)
+#define PLL_PWR_ON BIT(0)
+#define PLL_ISO_EN BIT(1)
+#define PLL_PCW_CHG BIT(31)
+#define RST_BAR_MASK BIT(24)
+#define AUDPLL_TUNER_EN BIT(31)
+
+static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 };
+
+static u32 calc_pll_vco_freq(
+ u32 fin,
+ u32 pcw,
+ u32 vcodivsel,
+ u32 prediv,
+ u32 pcwfbits)
+{
+ /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */
+ u64 vco = fin;
+ u8 c;
+
+ vco = vco * pcw * vcodivsel;
+ do_div(vco, prediv);
+
+ if (vco & GENMASK(pcwfbits - 1, 0))
+ c = 1;
+
+ vco >>= pcwfbits;
+
+ if (c)
+ ++vco;
+
+ return (u32)vco;
+}
+
+static u32 freq_limit(u32 freq)
+{
+ static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
+ static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 62.5 MHz */
+
+ if (freq <= freq_min)
+ freq = freq_min + 16;
+ else if (freq > freq_max)
+ freq = freq_max;
+
+ return freq;
+}
+
+static int calc_pll_freq_cfg(
+ u32 *pcw,
+ u32 *postdiv_idx,
+ u32 freq,
+ u32 fin,
+ int pcwfbits)
+{
+ static const u64 freq_max = 3000UL * 1000 * 1000; /* 3000 MHz */
+ static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */
+ static const u64 postdiv[] = { 1, 2, 4, 8, 16 };
+ u64 n_info;
+ u32 idx;
+
+ pr_debug("freq: %u\n", freq);
+
+ /* search suitable postdiv */
+ for (idx = *postdiv_idx;
+ idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min;
+ idx++)
+ ;
+
+ if (idx >= ARRAY_SIZE(postdiv))
+ return -EINVAL; /* freq is out of range (too low) */
+ else if (postdiv[idx] * freq > freq_max)
+ return -EINVAL; /* freq is out of range (too high) */
+
+ /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */
+ n_info = (postdiv[idx] * freq) << pcwfbits;
+ do_div(n_info, fin);
+
+ *postdiv_idx = idx;
+ *pcw = (u32)n_info;
+
+ return 0;
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ int r;
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 1;
+#endif
+
+ r = (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0;
+
+ pr_debug("%d: %s\n", r, __clk_get_name(hw->clk));
+
+ return r;
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ pr_debug("%s\n", __clk_get_name(hw->clk));
+
+ mtk_clk_lock(flags);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
+ writel_relaxed(r , pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ mtk_clk_unlock(flags);
+
+ return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return;
+#endif
+ pr_debug("%s: PLL_AO: %d\n",
+ __clk_get_name(hw->clk), !!(pll->flags & PLL_AO));
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ mtk_clk_lock(flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+
+ mtk_clk_unlock(flags);
+}
+
+static long clk_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: rate=%lu\n", __func__, __clk_get_name(hw->clk),
+ rate);
+ return 0;
+#endif
+ pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate);
+
+ *prate = *prate ? *prate : 26000000;
+ rate = freq_limit(rate);
+ calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+#define SDM_PLL_POSTDIV_H 6
+#define SDM_PLL_POSTDIV_L 4
+#define SDM_PLL_POSTDIV_MASK GENMASK(SDM_PLL_POSTDIV_H, SDM_PLL_POSTDIV_L)
+#define SDM_PLL_PCW_H 20
+#define SDM_PLL_PCW_L 0
+#define SDM_PLL_PCW_MASK GENMASK(SDM_PLL_PCW_H, SDM_PLL_PCW_L)
+
+static unsigned long clk_sdm_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: parent_rate=%lu\n", __func__,
+ __clk_get_name(hw->clk), parent_rate);
+ return 0;
+#endif
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 posdiv = (con0 & SDM_PLL_POSTDIV_MASK) >> SDM_PLL_POSTDIV_L;
+ u32 pcw = (con1 & SDM_PLL_PCW_MASK) >> SDM_PLL_PCW_L;
+ u32 pcwfbits = 14;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk));
+
+ return r;
+}
+
+static void clk_sdm_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ mtk_clk_lock(flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~SDM_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << SDM_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~SDM_PLL_PCW_MASK;
+ con1 |= pcw << SDM_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ mtk_clk_unlock(flags);
+}
+
+static int clk_sdm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits);
+
+ pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n",
+ __clk_get_name(hw->clk), rate, pcw, postdiv_idx);
+
+ if (r == 0)
+ clk_sdm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt_clk_sdm_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_sdm_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_sdm_pll_set_rate,
+};
+
+#define ARM_PLL_POSTDIV_H 26
+#define ARM_PLL_POSTDIV_L 24
+#define ARM_PLL_POSTDIV_MASK GENMASK(ARM_PLL_POSTDIV_H, ARM_PLL_POSTDIV_L)
+#define ARM_PLL_PCW_H 20
+#define ARM_PLL_PCW_L 0
+#define ARM_PLL_PCW_MASK GENMASK(ARM_PLL_PCW_H, ARM_PLL_PCW_L)
+
+static unsigned long clk_arm_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 posdiv = (con1 & ARM_PLL_POSTDIV_MASK) >> ARM_PLL_POSTDIV_L;
+ u32 pcw = (con1 & ARM_PLL_PCW_MASK) >> ARM_PLL_PCW_L;
+ u32 pcwfbits = 14;
+
+ u32 vco_freq;
+ unsigned long r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk));
+
+ return r;
+}
+
+static void clk_arm_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ mtk_clk_lock(flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* postdiv */
+ con1 &= ~ARM_PLL_POSTDIV_MASK;
+ con1 |= postdiv_idx << ARM_PLL_POSTDIV_L;
+
+ /* pcw */
+ con1 &= ~ARM_PLL_PCW_MASK;
+ con1 |= pcw << ARM_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ mtk_clk_unlock(flags);
+}
+
+static int clk_arm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits);
+
+ pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n",
+ __clk_get_name(hw->clk), rate, pcw, postdiv_idx);
+
+ if (r == 0)
+ clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt_clk_arm_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_arm_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_arm_pll_set_rate,
+};
+
+static long clk_mm_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate);
+
+ if (rate <= 702000000)
+ postdiv = 2;
+
+ *prate = *prate ? *prate : 26000000;
+ rate = freq_limit(rate);
+ calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int clk_mm_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 14;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ if (rate <= 702000000)
+ postdiv_idx = 2;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits);
+
+ pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n",
+ __clk_get_name(hw->clk), rate, pcw, postdiv_idx);
+
+ if (r == 0)
+ clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt_clk_mm_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_arm_pll_recalc_rate,
+ .round_rate = clk_mm_pll_round_rate,
+ .set_rate = clk_mm_pll_set_rate,
+};
+
+static int clk_univ_pll_enable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ pr_debug("%s\n", __clk_get_name(hw->clk));
+
+ mtk_clk_lock(flags);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ mtk_clk_unlock(flags);
+
+ return 0;
+}
+
+static void clk_univ_pll_disable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return;
+#endif
+ pr_debug("%s: PLL_AO: %d\n",
+ __clk_get_name(hw->clk), !!(pll->flags & PLL_AO));
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ mtk_clk_lock(flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ mtk_clk_unlock(flags);
+}
+
+#define UNIV_PLL_POSTDIV_H 6
+#define UNIV_PLL_POSTDIV_L 4
+#define UNIV_PLL_POSTDIV_MASK GENMASK(UNIV_PLL_POSTDIV_H, UNIV_PLL_POSTDIV_L)
+#define UNIV_PLL_FBKDIV_H 20
+#define UNIV_PLL_FBKDIV_L 14
+#define UNIV_PLL_FBKDIV_MASK GENMASK(UNIV_PLL_FBKDIV_H, UNIV_PLL_FBKDIV_L)
+
+static unsigned long clk_univ_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 fbkdiv = (con1 & UNIV_PLL_FBKDIV_MASK) >> UNIV_PLL_FBKDIV_L;
+ u32 posdiv = (con0 & UNIV_PLL_POSTDIV_MASK) >> UNIV_PLL_POSTDIV_L;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = parent_rate * fbkdiv;
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk));
+
+ return r;
+}
+
+static void clk_univ_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ mtk_clk_lock(flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* postdiv */
+ con0 &= ~UNIV_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << UNIV_PLL_POSTDIV_L;
+
+ /* fkbdiv */
+ con1 &= ~UNIV_PLL_FBKDIV_MASK;
+ con1 |= pcw << UNIV_PLL_FBKDIV_L;
+
+ writel_relaxed(con0, con0_addr);
+ writel_relaxed(con1, con1_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ mtk_clk_unlock(flags);
+}
+
+static long clk_univ_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 0;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate);
+
+ *prate = *prate ? *prate : 26000000;
+ rate = freq_limit(rate);
+ calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = *prate * pcw;
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int clk_univ_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 0;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits);
+
+ pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n",
+ __clk_get_name(hw->clk), rate, pcw, postdiv_idx);
+
+ if (r == 0)
+ clk_univ_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt_clk_univ_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_univ_pll_enable,
+ .disable = clk_univ_pll_disable,
+ .recalc_rate = clk_univ_pll_recalc_rate,
+ .round_rate = clk_univ_pll_round_rate,
+ .set_rate = clk_univ_pll_set_rate,
+};
+
+static int clk_aud_pll_enable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ pr_debug("%s\n", __clk_get_name(hw->clk));
+
+ mtk_clk_lock(flags);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+ wmb(); /* sync write before delay */
+ udelay(1);
+
+ r = readl_relaxed(pll->base_addr) | pll->en_mask;
+ writel_relaxed(r, pll->base_addr);
+
+ r = readl_relaxed(pll->base_addr + 16) | AUDPLL_TUNER_EN;
+ writel_relaxed(r, pll->base_addr + 16);
+ wmb(); /* sync write before delay */
+ udelay(20);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) | RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ mtk_clk_unlock(flags);
+
+ return 0;
+}
+
+static void clk_aud_pll_disable(struct clk_hw *hw)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return;
+#endif
+ pr_debug("%s: PLL_AO: %d\n",
+ __clk_get_name(hw->clk), !!(pll->flags & PLL_AO));
+
+ if (pll->flags & PLL_AO)
+ return;
+
+ mtk_clk_lock(flags);
+
+ if (pll->flags & HAVE_RST_BAR) {
+ r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK;
+ writel_relaxed(r, pll->base_addr);
+ }
+
+ r = readl_relaxed(pll->base_addr + 8) & ~AUDPLL_TUNER_EN;
+ writel_relaxed(r, pll->base_addr + 8);
+
+ r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN;
+ writel_relaxed(r, pll->base_addr);
+
+ r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN;
+ writel_relaxed(r, pll->pwr_addr);
+
+ r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON;
+ writel_relaxed(r, pll->pwr_addr);
+
+ mtk_clk_unlock(flags);
+}
+
+#define AUD_PLL_POSTDIV_H 6
+#define AUD_PLL_POSTDIV_L 4
+#define AUD_PLL_POSTDIV_MASK GENMASK(AUD_PLL_POSTDIV_H, AUD_PLL_POSTDIV_L)
+#define AUD_PLL_PCW_H 30
+#define AUD_PLL_PCW_L 0
+#define AUD_PLL_PCW_MASK GENMASK(AUD_PLL_PCW_H, AUD_PLL_PCW_L)
+
+static unsigned long clk_aud_pll_recalc_rate(
+ struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ u32 con0 = readl_relaxed(pll->base_addr);
+ u32 con1 = readl_relaxed(pll->base_addr + 4);
+
+ u32 posdiv = (con0 & AUD_PLL_POSTDIV_MASK) >> AUD_PLL_POSTDIV_L;
+ u32 pcw = (con1 & AUD_PLL_PCW_MASK) >> AUD_PLL_PCW_L;
+ u32 pcwfbits = 24;
+
+ u32 vco_freq;
+ unsigned long r;
+
+ parent_rate = parent_rate ? parent_rate : 26000000;
+
+ vco_freq = calc_pll_vco_freq(parent_rate, pcw, 1, 1, pcwfbits);
+ r = (vco_freq + pll_posdiv_map[posdiv] - 1) / pll_posdiv_map[posdiv];
+
+ pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk));
+
+ return r;
+}
+
+static void clk_aud_pll_set_rate_regs(
+ struct clk_hw *hw,
+ u32 pcw,
+ u32 postdiv_idx)
+{
+ unsigned long flags = 0;
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ void __iomem *con0_addr = pll->base_addr;
+ void __iomem *con1_addr = pll->base_addr + 4;
+ void __iomem *con2_addr = pll->base_addr + 8;
+ u32 con0;
+ u32 con1;
+ u32 pll_en;
+
+ mtk_clk_lock(flags);
+
+ con0 = readl_relaxed(con0_addr);
+ con1 = readl_relaxed(con1_addr);
+
+ pll_en = con0 & PLL_BASE_EN;
+
+ /* set postdiv */
+ con0 &= ~AUD_PLL_POSTDIV_MASK;
+ con0 |= postdiv_idx << AUD_PLL_POSTDIV_L;
+ writel_relaxed(con0, con0_addr);
+
+ /* set pcw */
+ con1 &= ~AUD_PLL_PCW_MASK;
+ con1 |= pcw << AUD_PLL_PCW_L;
+
+ if (pll_en)
+ con1 |= PLL_PCW_CHG;
+
+ writel_relaxed(con1, con1_addr);
+ writel_relaxed(con1 + 1, con2_addr);
+
+ if (pll_en) {
+ wmb(); /* sync write before delay */
+ udelay(20);
+ }
+
+ mtk_clk_unlock(flags);
+}
+
+static long clk_aud_pll_round_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv = 0;
+ u32 r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: S\n", __func__, __clk_get_name(hw->clk));
+ return 0;
+#endif
+ pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate);
+
+ *prate = *prate ? *prate : 26000000;
+ rate = freq_limit(rate);
+ calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits);
+
+ r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits);
+ r = (r + pll_posdiv_map[postdiv] - 1) / pll_posdiv_map[postdiv];
+
+ return r;
+}
+
+static int clk_aud_pll_set_rate(
+ struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 pcwfbits = 24;
+ u32 pcw = 0;
+ u32 postdiv_idx = 0;
+ int r;
+
+#ifdef Bring_Up
+ pr_debug("[CCF] %s: %s: rate=%lu, parent_rate=%lu\n", __func__,
+ __clk_get_name(hw->clk), rate, parent_rate);
+ return 0;
+#endif
+ parent_rate = parent_rate ? parent_rate : 26000000;
+ r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits);
+
+ pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n",
+ __clk_get_name(hw->clk), rate, pcw, postdiv_idx);
+
+ if (r == 0)
+ clk_aud_pll_set_rate_regs(hw, pcw, postdiv_idx);
+
+ return r;
+}
+
+const struct clk_ops mt_clk_aud_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_aud_pll_enable,
+ .disable = clk_aud_pll_disable,
+ .recalc_rate = clk_aud_pll_recalc_rate,
+ .round_rate = clk_aud_pll_round_rate,
+ .set_rate = clk_aud_pll_set_rate,
+};
diff --git a/drivers/clk/mediatek/clk-mt6735-pll.h b/drivers/clk/mediatek/clk-mt6735-pll.h
new file mode 100644
index 000000000..dc1e381b6
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-pll.h
@@ -0,0 +1,14 @@
+#ifndef __DRV_CLK_MT6735_PLL_H
+#define __DRV_CLK_MT6735_PLL_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+extern const struct clk_ops mt_clk_sdm_pll_ops;
+extern const struct clk_ops mt_clk_arm_pll_ops;
+extern const struct clk_ops mt_clk_mm_pll_ops;
+extern const struct clk_ops mt_clk_univ_pll_ops;
+extern const struct clk_ops mt_clk_aud_pll_ops;
+
+#endif /* __DRV_CLK_MT6735_PLL_H */
diff --git a/drivers/clk/mediatek/clk-mt6735.c b/drivers/clk/mediatek/clk-mt6735.c
new file mode 100644
index 000000000..1931f5ba6
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735.c
@@ -0,0 +1,1328 @@
+/*
+* Copyright (c) 2014 MediaTek Inc.
+* Author: James Liao <jamesjj.liao@mediatek.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.
+*
+* 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/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-gate.h"
+#include "clk-mt6735-pll.h"
+
+#include <dt-bindings/clock/mt6735-clk.h>
+
+#ifdef Bring_Up
+#ifdef pr_debug
+#undef pr_debug
+#define pr_debug printk
+#endif
+#endif /* Bring_Up */
+
+/*
+ * platform clocks
+ */
+
+/* ROOT */
+#define clk_null "clk_null"
+#define clk26m "clk26m"
+#define clk32k "clk32k"
+
+#define clkph_mck_o "clkph_mck_o"
+#define dpi_ck "dpi_ck"
+
+/* PLL */
+#define armpll "armpll"
+#define mainpll "mainpll"
+#define msdcpll "msdcpll"
+#define univpll "univpll"
+#define mmpll "mmpll"
+#define vencpll "vencpll"
+#define tvdpll "tvdpll"
+#define apll1 "apll1"
+#define apll2 "apll2"
+
+#define armca7pll_754m "armca7pll_754m"
+#define armca7pll_502m "armca7pll_502m"
+#define apll1_180p633m apll1
+#define apll2_196p608m apll2
+#define mmpll_455m mmpll
+#define msdcpll_806m msdcpll
+#define main_h546m "main_h546m"
+#define main_h364m "main_h364m"
+#define main_h218p4m "main_h218p4m"
+#define main_h156m "main_h156m"
+#define tvdpll_445p5m "tvdpll_445p5m"
+#define tvdpll_594m "tvdpll_594m"
+#define univ_624m "univ_624m"
+#define univ_416m "univ_416m"
+#define univ_249p6m "univ_249p6m"
+#define univ_178p3m "univ_178p3m"
+#define univ_48m "univ_48m"
+#define vcodecpll_370p5 "vcodecpll_370p5"
+#define vcodecpll_494m vcodecpll
+#define vencpll_380m vencpll
+#define lvdspll_ck lvdspll
+
+/* DIV */
+#define ad_apll1_ck "ad_apll1_ck"
+#define ad_sys_26m_ck "ad_sys_26m_ck"
+#define ad_sys_26m_d2 "ad_sys_26m_d2"
+#define dmpll_ck "dmpll_ck"
+#define dmpll_d2 "dmpll_d2"
+#define dmpll_d4 "dmpll_d4"
+#define dmpll_d8 "dmpll_d8"
+#define dpi_ck "dpi_ck"
+#define mmpll_ck "mmpll_ck"
+#define msdcpll_ck "msdcpll_ck"
+#define msdcpll_d16 "msdcpll_d16"
+#define msdcpll_d2 "msdcpll_d2"
+#define msdcpll_d4 "msdcpll_d4"
+#define msdcpll_d8 "msdcpll_d8"
+#define syspll_d2 "syspll_d2"
+#define syspll_d3 "syspll_d3"
+#define syspll_d5 "syspll_d5"
+#define syspll1_d16 "syspll1_d16"
+#define syspll1_d2 "syspll1_d2"
+#define syspll1_d4 "syspll1_d4"
+#define syspll1_d8 "syspll1_d8"
+#define syspll2_d2 "syspll2_d2"
+#define syspll2_d4 "syspll2_d4"
+#define syspll3_d2 "syspll3_d2"
+#define syspll3_d4 "syspll3_d4"
+#define syspll4_d2 "syspll4_d2"
+#define syspll4_d2_d8 "syspll4_d2_d8"
+#define syspll4_d4 "syspll4_d4"
+#define tvdpll_ck "tvdpll_ck"
+#define tvdpll_d2 "tvdpll_d2"
+#define tvdpll_d4 "tvdpll_d4"
+#define univpll_d2 "univpll_d2"
+#define univpll_d26 "univpll_d26"
+#define univpll_d3 "univpll_d3"
+#define univpll_d5 "univpll_d5"
+#define univpll1_d2 "univpll1_d2"
+#define univpll1_d4 "univpll1_d4"
+#define univpll1_d8 "univpll1_d8"
+#define univpll2_d2 "univpll2_d2"
+#define univpll2_d4 "univpll2_d4"
+#define univpll2_d8 "univpll2_d8"
+#define univpll3_d2 "univpll3_d2"
+#define univpll3_d4 "univpll3_d4"
+#define vencpll_ck "vencpll_ck"
+#define vencpll_d3 "vencpll_d3"
+#define whpll_audio_ck "whpll_audio_ck"
+
+/* TOP */
+#define axi_sel "axi_sel"
+#define mem_sel "mem_sel"
+#define ddrphycfg_sel "ddrphycfg_sel"
+#define mm_sel "mm_sel"
+#define pwm_sel "pwm_sel"
+#define vdec_sel "vdec_sel"
+#define mfg_sel "mfg_sel"
+#define camtg_sel "camtg_sel"
+#define uart_sel "uart_sel"
+#define spi_sel "spi_sel"
+#define usb20_sel "usb20_sel"
+#define msdc50_0_sel "msdc50_0_sel"
+#define msdc30_0_sel "msdc30_0_sel"
+#define msdc30_1_sel "msdc30_1_sel"
+#define msdc30_2_sel "msdc30_2_sel"
+#define msdc30_3_sel "msdc30_3_sel"
+#define audio_sel "audio_sel"
+#define aud_intbus_sel "aud_intbus_sel"
+#define pmicspi_sel "pmicspi_sel"
+#define scp_sel "scp_sel"
+#define atb_sel "atb_sel"
+#define dpi0_sel "dpi0_sel"
+#define scam_sel "scam_sel"
+#define mfg13m_sel "mfg13m_sel"
+#define aud_1_sel "aud_1_sel"
+#define aud_2_sel "aud_2_sel"
+#define irda_sel "irda_sel"
+#define irtx_sel "irtx_sel"
+#define disppwm_sel "disppwm_sel"
+
+/* INFRA */
+#define infra_dbgclk "infra_dbgclk"
+#define infra_gce "infra_gce"
+#define infra_trbg "infra_trbg"
+#define infra_cpum "infra_cpum"
+#define infra_devapc "infra_devapc"
+#define infra_audio "infra_audio"
+#define infra_gcpu "infra_gcpu"
+#define infra_l2csram "infra_l2csram"
+#define infra_m4u "infra_m4u"
+#define infra_cldma "infra_cldma"
+#define infra_connmcubus "infra_connmcubus"
+#define infra_kp "infra_kp"
+#define infra_apxgpt "infra_apxgpt"
+#define infra_sej "infra_sej"
+#define infra_ccif0ap "infra_ccif0ap"
+#define infra_ccif1ap "infra_ccif1ap"
+#define infra_pmicspi "infra_pmicspi"
+#define infra_pmicwrap "infra_pmicwrap"
+
+/* PERI */
+#define peri_disp_pwm "peri_disp_pwm"
+#define peri_therm "peri_therm"
+#define peri_pwm1 "peri_pwm1"
+#define peri_pwm2 "peri_pwm2"
+#define peri_pwm3 "peri_pwm3"
+#define peri_pwm4 "peri_pwm4"
+#define peri_pwm5 "peri_pwm5"
+#define peri_pwm6 "peri_pwm6"
+#define peri_pwm7 "peri_pwm7"
+#define peri_pwm "peri_pwm"
+#define peri_usb0 "peri_usb0"
+#define peri_irda "peri_irda"
+#define peri_apdma "peri_apdma"
+#define peri_msdc30_0 "peri_msdc30_0"
+#define peri_msdc30_1 "peri_msdc30_1"
+#define peri_msdc30_2 "peri_msdc30_2"
+#define peri_msdc30_3 "peri_msdc30_3"
+#define peri_uart0 "peri_uart0"
+#define peri_uart1 "peri_uart1"
+#define peri_uart2 "peri_uart2"
+#define peri_uart3 "peri_uart3"
+#define peri_uart4 "peri_uart4"
+#define peri_btif "peri_btif"
+#define peri_i2c0 "peri_i2c0"
+#define peri_i2c1 "peri_i2c1"
+#define peri_i2c2 "peri_i2c2"
+#define peri_i2c3 "peri_i2c3"
+#define peri_auxadc "peri_auxadc"
+#define peri_spi0 "peri_spi0"
+#define peri_irtx "peri_irtx"
+
+/* MFG */
+#define mfg_bg3d "mfg_bg3d"
+
+/* IMG */
+#define img_image_larb2_smi "img_image_larb2_smi"
+#define img_image_cam_smi "img_image_cam_smi"
+#define img_image_cam_cam "img_image_cam_cam"
+#define img_image_sen_tg "img_image_sen_tg"
+#define img_image_sen_cam "img_image_sen_cam"
+#define img_image_cam_sv "img_image_cam_sv"
+#define img_image_sufod "img_image_sufod"
+#define img_image_fd "img_image_fd"
+
+/* MM_SYS */
+#define mm_disp0_smi_common "mm_disp0_smi_common"
+#define mm_disp0_smi_larb0 "mm_disp0_smi_larb0"
+#define mm_disp0_cam_mdp "mm_disp0_cam_mdp"
+#define mm_disp0_mdp_rdma "mm_disp0_mdp_rdma"
+#define mm_disp0_mdp_rsz0 "mm_disp0_mdp_rsz0"
+#define mm_disp0_mdp_rsz1 "mm_disp0_mdp_rsz1"
+#define mm_disp0_mdp_tdshp "mm_disp0_mdp_tdshp"
+#define mm_disp0_mdp_wdma "mm_disp0_mdp_wdma"
+#define mm_disp0_mdp_wrot "mm_disp0_mdp_wrot"
+#define mm_disp0_fake_eng "mm_disp0_fake_eng"
+#define mm_disp0_disp_ovl0 "mm_disp0_disp_ovl0"
+#define mm_disp0_disp_rdma0 "mm_disp0_disp_rdma0"
+#define mm_disp0_disp_rdma1 "mm_disp0_disp_rdma1"
+#define mm_disp0_disp_wdma0 "mm_disp0_disp_wdma0"
+#define mm_disp0_disp_color "mm_disp0_disp_color"
+#define mm_disp0_disp_ccorr "mm_disp0_disp_ccorr"
+#define mm_disp0_disp_aal "mm_disp0_disp_aal"
+#define mm_disp0_disp_gamma "mm_disp0_disp_gamma"
+#define mm_disp0_disp_dither "mm_disp0_disp_dither"
+#define mm_disp1_dsi_engine "mm_disp1_dsi_engine"
+#define mm_disp1_dsi_digital "mm_disp1_dsi_digital"
+#define mm_disp1_dpi_engine "mm_disp1_dpi_engine"
+#define mm_disp1_dpi_pixel "mm_disp1_dpi_pixel"
+
+/* VDEC */
+#define vdec0_vdec "vdec0_vdec"
+#define vdec1_larb "vdec1_larb"
+
+/* VENC */
+#define venc_larb "venc_larb"
+#define venc_venc "venc_venc"
+#define venc_jpgenc "venc_jpgenc"
+#define venc_jpgdec "venc_jpgdec"
+
+/* VENCLT */
+#define venclt_cke0 "venclt_cke0"
+#define venclt_cke1 "venclt_cke1"
+
+/* AUDIO */
+#define audio_afe "audio_afe"
+#define audio_i2s "audio_i2s"
+#define audio_22m "audio_22m"
+#define audio_24m "audio_24m"
+#define audio_apll2_tuner "audio_apll2_tuner"
+#define audio_apll_tuner "audio_apll_tuner"
+#define audio_adc "audio_adc"
+#define audio_dac "audio_dac"
+#define audio_dac_predis "audio_dac_predis"
+#define audio_tml "audio_tml"
+
+struct mtk_fixed_factor {
+ int id;
+ const char *name;
+ const char *parent_name;
+ int mult;
+ int div;
+};
+
+#define FACTOR(_id, _name, _parent, _mult, _div) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .mult = _mult, \
+ .div = _div, \
+ }
+
+static void __init init_factors(struct mtk_fixed_factor *clks, int num,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < num; i++) {
+ struct mtk_fixed_factor *ff = &clks[i];
+
+ clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name,
+ 0, ff->mult, ff->div);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ ff->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[ff->id] = clk;
+
+ pr_debug("factor %3d: %s\n", i, ff->name);
+ }
+}
+
+static struct mtk_fixed_factor root_clk_alias[] __initdata = {
+ FACTOR(TOP_CLKPH_MCK_O, clkph_mck_o, clk_null, 1, 1),
+ FACTOR(TOP_DPI_CK, dpi_ck, clk_null, 1, 1),
+};
+
+static void __init init_clk_root_alias(struct clk_onecell_data *clk_data)
+{
+ init_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data);
+}
+
+static struct mtk_fixed_factor top_divs[] __initdata = {
+ FACTOR(TOP_UNIVPLL_D2, univpll_d2, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL_D3, univpll_d3, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL1_D2, univpll1_d2, univpll, 1, 1),
+ FACTOR(TOP_AD_APLL1_CK, ad_apll1_ck, apll1, 1, 1),
+ FACTOR(TOP_AD_SYS_26M_CK, ad_sys_26m_ck, clk26m, 1, 1),
+ FACTOR(TOP_AD_SYS_26M_D2, ad_sys_26m_d2, clk26m, 1, 1),
+ FACTOR(TOP_DMPLL_CK, dmpll_ck, clkph_mck_o, 1, 1),
+ FACTOR(TOP_DMPLL_D2, dmpll_d2, clkph_mck_o, 1, 1),
+ FACTOR(TOP_DMPLL_D4, dmpll_d4, clkph_mck_o, 1, 1),
+ FACTOR(TOP_DMPLL_D8, dmpll_d8, clkph_mck_o, 1, 1),
+ FACTOR(TOP_MMPLL_CK, mmpll_ck, mmpll, 1, 1),
+ FACTOR(TOP_MSDCPLL_CK, msdcpll_ck, msdcpll, 1, 1),
+ FACTOR(TOP_MSDCPLL_D16, msdcpll_d16, msdcpll, 1, 1),
+ FACTOR(TOP_MSDCPLL_D2, msdcpll_d2, msdcpll, 1, 1),
+ FACTOR(TOP_MSDCPLL_D4, msdcpll_d4, msdcpll, 1, 1),
+ FACTOR(TOP_MSDCPLL_D8, msdcpll_d8, msdcpll, 1, 1),
+ FACTOR(TOP_SYSPLL_D2, syspll_d2, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL_D3, syspll_d3, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL_D5, syspll_d5, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL1_D16, syspll1_d16, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL1_D2, syspll1_d2, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL1_D4, syspll1_d4, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL1_D8, syspll1_d8, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL2_D2, syspll2_d2, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL2_D4, syspll2_d4, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL3_D2, syspll3_d2, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL3_D4, syspll3_d4, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL4_D2, syspll4_d2, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL4_D2_D8, syspll4_d2_d8, mainpll, 1, 1),
+ FACTOR(TOP_SYSPLL4_D4, syspll4_d4, mainpll, 1, 1),
+ FACTOR(TOP_TVDPLL_CK, tvdpll_ck, tvdpll, 1, 1),
+ FACTOR(TOP_TVDPLL_D2, tvdpll_d2, tvdpll, 1, 1),
+ FACTOR(TOP_TVDPLL_D4, tvdpll_d4, tvdpll, 1, 1),
+ FACTOR(TOP_UNIVPLL_D26, univpll_d26, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL_D3, univpll_d3, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL_D5, univpll_d5, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL1_D4, univpll1_d4, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL1_D8, univpll1_d8, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL2_D2, univpll2_d2, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL2_D4, univpll2_d4, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL2_D8, univpll2_d8, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL3_D2, univpll3_d2, univpll, 1, 1),
+ FACTOR(TOP_UNIVPLL3_D4, univpll3_d4, univpll, 1, 1),
+ FACTOR(TOP_VENCPLL_CK, vencpll_ck, vencpll, 1, 1),
+ FACTOR(TOP_VENCPLL_D3, vencpll_d3, vencpll, 1, 1),
+ FACTOR(TOP_WHPLL_AUDIO_CK, whpll_audio_ck, clk_null, 1, 1),
+};
+
+static void __init init_clk_top_div(struct clk_onecell_data *clk_data)
+{
+ init_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
+}
+
+static const char *axi_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll_d5,
+ syspll1_d4,
+ univpll_d5,
+ univpll2_d2,
+ dmpll_ck,
+ dmpll_d2};
+
+static const char *mem_parents[] __initconst = {
+ clk26m,
+ dmpll_ck};
+
+static const char *ddrphycfg_parents[] __initconst = {
+ clk26m,
+ syspll1_d8};
+
+static const char *mm_parents[] __initconst = {
+ clk26m,
+ vencpll_ck,
+ syspll1_d2,
+ syspll_d5,
+ syspll1_d4,
+ univpll_d5,
+ univpll2_d2,
+ dmpll_ck};
+
+static const char *pwm_parents[] __initconst = {
+ clk26m,
+ univpll2_d4,
+ univpll3_d2,
+ univpll1_d4};
+
+static const char *vdec_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll_d5,
+ syspll1_d4,
+ univpll_d5,
+ syspll_d2,
+ syspll2_d2,
+ msdcpll_d2};
+
+static const char *mfg_parents[] __initconst = {
+ clk26m,
+ mmpll_ck,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ clk26m,
+ syspll_d3,
+ syspll1_d2,
+ syspll_d5,
+ univpll_d3,
+ univpll1_d2
+};
+
+static const char *camtg_parents[] __initconst = {
+ clk26m,
+ univpll_d26,
+ univpll2_d2,
+ syspll3_d2,
+ syspll3_d4,
+ msdcpll_d4
+};
+
+static const char *uart_parents[] __initconst = {
+ clk26m,
+ univpll2_d8
+};
+
+static const char *spi_parents[] __initconst = {
+ clk26m,
+ syspll3_d2,
+ msdcpll_d8,
+ syspll2_d4,
+ syspll4_d2,
+ univpll2_d4,
+ univpll1_d8
+};
+
+static const char *usb20_parents[] __initconst = {
+ clk26m,
+ univpll1_d8,
+ univpll3_d4
+};
+
+static const char *msdc50_0_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll2_d2,
+ syspll4_d2,
+ univpll_d5,
+ univpll1_d4
+};
+
+static const char *msdc30_0_parents[] __initconst = {
+ clk26m,
+ msdcpll_ck,
+ msdcpll_d2,
+ msdcpll_d4,
+ syspll2_d2,
+ syspll1_d4,
+ univpll1_d4,
+ univpll_d3,
+ univpll_d26,
+ syspll2_d4,
+ univpll_d2
+};
+
+static const char *msdc30_1_parents[] __initconst = {
+ clk26m,
+ univpll2_d2,
+ msdcpll_d4,
+ syspll2_d2,
+ syspll1_d4,
+ univpll1_d4,
+ univpll_d26,
+ syspll2_d4
+};
+
+static const char *msdc30_2_parents[] __initconst = {
+ clk26m,
+ univpll2_d2,
+ msdcpll_d4,
+ syspll2_d2,
+ syspll1_d4,
+ univpll1_d4,
+ univpll_d26,
+ syspll2_d4
+};
+
+static const char *msdc30_3_parents[] __initconst = {
+ clk26m,
+ univpll2_d2,
+ msdcpll_d4,
+ syspll2_d2,
+ syspll1_d4,
+ univpll1_d4,
+ univpll_d26,
+ msdcpll_d16,
+ syspll2_d4
+};
+
+static const char *audio_parents[] __initconst = {
+ clk26m,
+ syspll3_d4,
+ syspll4_d4,
+ syspll1_d16
+};
+
+static const char *aud_intbus_parents[] __initconst = {
+ clk26m,
+ syspll1_d4,
+ syspll4_d2,
+ dmpll_d4
+};
+
+static const char *pmicspi_parents[] __initconst = {
+ clk26m,
+ syspll1_d8,
+ syspll3_d4,
+ syspll1_d16,
+ univpll3_d4,
+ univpll_d26,
+ dmpll_d4,
+ dmpll_d8
+};
+
+static const char *scp_parents[] __initconst = {
+ clk26m,
+ syspll1_d8,
+ dmpll_d2,
+ dmpll_d4
+};
+
+static const char *atb_parents[] __initconst = {
+ clk26m,
+ syspll1_d2,
+ syspll_d5,
+ dmpll_ck
+};
+
+static const char *dpi0_parents[] __initconst = {
+ clk26m,
+ tvdpll_ck,
+ tvdpll_d2,
+ tvdpll_d4,
+ dpi_ck
+};
+
+static const char *scam_parents[] __initconst = {
+ clk26m,
+ syspll3_d2,
+ univpll2_d4,
+ vencpll_d3
+};
+
+static const char *mfg13m_parents[] __initconst = {
+ clk26m,
+ ad_sys_26m_d2
+};
+
+static const char *aud_1_parents[] __initconst = {
+ clk26m,
+ ad_apll1_ck
+};
+
+static const char *aud_2_parents[] __initconst = {
+ clk26m,
+ whpll_audio_ck
+};
+
+static const char *irda_parents[] __initconst = {
+ clk26m,
+ univpll2_d4
+};
+
+static const char *irtx_parents[] __initconst = {
+ clk26m,
+ ad_sys_26m_ck
+};
+
+static const char *disppwm_parents[] __initconst = {
+ clk26m,
+ univpll2_d4,
+ syspll4_d2_d8,
+ ad_sys_26m_ck
+};
+
+struct mtk_mux {
+ int id;
+ const char *name;
+ uint32_t reg;
+ int shift;
+ int width;
+ int gate;
+ const char **parent_names;
+ int num_parents;
+};
+
+#define MUX(_id, _name, _parents, _reg, _shift, _width, _gate) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .shift = _shift, \
+ .width = _width, \
+ .gate = _gate, \
+ .parent_names = (const char **)_parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ }
+
+static struct mtk_mux top_muxes[] __initdata = {
+ MUX(TOP_MUX_AXI, axi_sel, axi_parents, 0x0040, 0, 3, INVALID_MUX_GATE_BIT),
+ MUX(TOP_MUX_MEM, mem_sel, mem_parents, 0x0040, 8, 1, INVALID_MUX_GATE_BIT),
+ MUX(TOP_MUX_DDRPHY, ddrphycfg_sel, ddrphycfg_parents, 0x0040, 16, 1, INVALID_MUX_GATE_BIT),
+ MUX(TOP_MUX_MM, mm_sel, mm_parents, 0x0040, 24, 3, 31),
+ MUX(TOP_MUX_PWM, pwm_sel, pwm_parents, 0x0050, 0, 2, 7),
+ MUX(TOP_MUX_VDEC, vdec_sel, vdec_parents, 0x0050, 8, 3, 15),
+ MUX(TOP_MUX_MFG, mfg_sel, mfg_parents, 0x0050, 16, 4, 23),
+ MUX(TOP_MUX_CAMTG, camtg_sel, camtg_parents, 0x0050, 24, 3, 31),
+ MUX(TOP_MUX_UART, uart_sel, uart_parents, 0x0060, 0, 1, 7),
+ MUX(TOP_MUX_SPI, spi_sel, spi_parents, 0x0060, 8, 3, 15),
+ MUX(TOP_MUX_USB20, usb20_sel, usb20_parents, 0x0060, 16, 2, 23),
+ MUX(TOP_MUX_MSDC50_0, msdc50_0_sel, msdc50_0_parents, 0x0060, 24, 3, 31),
+ MUX(TOP_MUX_MSDC30_0, msdc30_0_sel, msdc30_0_parents, 0x0070, 0, 4, 7),
+ MUX(TOP_MUX_MSDC30_1, msdc30_1_sel, msdc30_1_parents, 0x0070, 8, 3, 15),
+ MUX(TOP_MUX_MSDC30_2, msdc30_2_sel, msdc30_2_parents, 0x0070, 16, 3, 23),
+ MUX(TOP_MUX_MSDC30_3, msdc30_3_sel, msdc30_3_parents, 0x0070, 24, 4, 31),
+ MUX(TOP_MUX_AUDIO, audio_sel, audio_parents, 0x0080, 0, 2, 7),
+ MUX(TOP_MUX_AUDINTBUS, aud_intbus_sel, aud_intbus_parents, 0x0080, 8, 2, 15),
+ MUX(TOP_MUX_PMICSPI, pmicspi_sel, pmicspi_parents, 0x0080, 16, 3, INVALID_MUX_GATE_BIT),
+ MUX(TOP_MUX_SCP, scp_sel, scp_parents, 0x0080, 24, 2, 31),
+ MUX(TOP_MUX_ATB, atb_sel, atb_parents, 0x0090, 0, 2, 7),
+ MUX(TOP_MUX_DPI0, dpi0_sel, dpi0_parents, 0x0090, 8, 3, 15),
+ MUX(TOP_MUX_SCAM, scam_sel, scam_parents, 0x0090, 16, 2, 23),
+ MUX(TOP_MUX_MFG13M, mfg13m_sel, mfg13m_parents, 0x0090, 24, 1, 31),
+ MUX(TOP_MUX_AUD1, aud_1_sel, aud_1_parents, 0x00a0, 0, 1, 7),
+ MUX(TOP_MUX_AUD2, aud_2_sel, aud_2_parents, 0x00a0, 8, 1, 15),
+ MUX(TOP_MUX_IRDA, irda_sel, irda_parents, 0x00a0, 16, 1, 23),
+ MUX(TOP_MUX_IRTX, irtx_sel, irtx_parents, 0x00a0, 24, 1, 31),
+ MUX(TOP_MUX_DISPPWM, disppwm_sel, disppwm_parents, 0x00b0, 0, 2, 7),
+};
+
+static void __init init_clk_topckgen(void __iomem *top_base,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < ARRAY_SIZE(top_muxes); i++) {
+ struct mtk_mux *mux = &top_muxes[i];
+
+ clk = mtk_clk_register_mux(mux->name,
+ mux->parent_names, mux->num_parents,
+ top_base + mux->reg, mux->shift, mux->width, mux->gate);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ mux->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[mux->id] = clk;
+
+ pr_debug("mux %3d: %s\n", i, mux->name);
+ }
+}
+
+struct mtk_pll {
+ int id;
+ const char *name;
+ const char *parent_name;
+ uint32_t reg;
+ uint32_t pwr_reg;
+ uint32_t en_mask;
+ unsigned int flags;
+ const struct clk_ops *ops;
+};
+
+#define PLL(_id, _name, _parent, _reg, _pwr_reg, _en_mask, _flags, _ops) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .ops = _ops, \
+ }
+
+static struct mtk_pll plls[] __initdata = {
+ PLL(APMIXED_ARMPLL, armpll, clk26m, 0x0200, 0x020C, 0x00000001, HAVE_PLL_HP, &mt_clk_arm_pll_ops),
+ PLL(APMIXED_MAINPLL, mainpll, clk26m, 0x0210, 0x021C, 0xF0000101, HAVE_PLL_HP, &mt_clk_sdm_pll_ops),
+ PLL(APMIXED_MSDCPLL, msdcpll, clk26m, 0x0240, 0x024C, 0x00000001, HAVE_PLL_HP, &mt_clk_sdm_pll_ops),
+ PLL(APMIXED_UNIVPLL, univpll, clk26m, 0x0220, 0x022C, 0xFC000001, HAVE_PLL_HP, &mt_clk_univ_pll_ops),
+ PLL(APMIXED_MMPLL, mmpll, clk26m, 0x0230, 0x023C, 0x00000001, HAVE_PLL_HP, &mt_clk_mm_pll_ops),
+ PLL(APMIXED_VENCPLL, vencpll, clk26m, 0x0250, 0x025C, 0x00000001, HAVE_PLL_HP, &mt_clk_sdm_pll_ops),
+ PLL(APMIXED_TVDPLL, tvdpll, clk26m, 0x0260, 0x026C, 0x00000001, HAVE_PLL_HP, &mt_clk_sdm_pll_ops),
+ PLL(APMIXED_APLL1, apll1, clk26m, 0x0270, 0x0280, 0x00000001, HAVE_PLL_HP, &mt_clk_aud_pll_ops),
+ PLL(APMIXED_APLL2, apll2, clk26m, 0x0284, 0x0294, 0x00000001, HAVE_PLL_HP, &mt_clk_aud_pll_ops),
+};
+
+static void __init init_clk_apmixedsys(void __iomem *apmixed_base,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < ARRAY_SIZE(plls); i++) {
+ struct mtk_pll *pll = &plls[i];
+
+ clk = mtk_clk_register_pll(pll->name, pll->parent_name,
+ apmixed_base + pll->reg,
+ apmixed_base + pll->pwr_reg,
+ pll->en_mask, pll->flags, pll->ops);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ pll->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[pll->id] = clk;
+
+ pr_debug("pll %3d: %s\n", i, pll->name);
+ }
+}
+
+struct mtk_gate_regs {
+ u32 sta_ofs;
+ u32 clr_ofs;
+ u32 set_ofs;
+};
+
+struct mtk_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ struct mtk_gate_regs *regs;
+ int shift;
+ uint32_t flags;
+};
+
+#define GATE(_id, _name, _parent, _regs, _shift, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &_regs, \
+ .shift = _shift, \
+ .flags = _flags, \
+ }
+
+static void __init init_clk_gates(
+ void __iomem *reg_base,
+ struct mtk_gate *clks, int num,
+ struct clk_onecell_data *clk_data)
+{
+ int i;
+ struct clk *clk;
+
+ for (i = 0; i < num; i++) {
+ struct mtk_gate *gate = &clks[i];
+
+ clk = mtk_clk_register_gate(gate->name, gate->parent_name,
+ reg_base + gate->regs->set_ofs,
+ reg_base + gate->regs->clr_ofs,
+ reg_base + gate->regs->sta_ofs,
+ gate->shift, gate->flags);
+
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ gate->name, PTR_ERR(clk));
+ continue;
+ }
+
+ if (clk_data)
+ clk_data->clks[gate->id] = clk;
+
+ pr_debug("gate %3d: %s\n", i, gate->name);
+ }
+}
+
+static struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = 0x0040,
+ .clr_ofs = 0x0044,
+ .sta_ofs = 0x0048,
+};
+
+static struct mtk_gate infra_clks[] __initdata = {
+ GATE(INFRA_DBGCLK, infra_dbgclk, axi_sel, infra_cg_regs, 0, 0),
+ GATE(INFRA_GCE, infra_gce, axi_sel, infra_cg_regs, 1, 0),
+ GATE(INFRA_TRBG, infra_trbg, axi_sel, infra_cg_regs, 2, 0),
+ GATE(INFRA_CPUM, infra_cpum, axi_sel, infra_cg_regs, 3, 0),
+ GATE(INFRA_DEVAPC, infra_devapc, axi_sel, infra_cg_regs, 4, 0),
+ GATE(INFRA_AUDIO, infra_audio, aud_intbus_sel, infra_cg_regs, 5, 0),
+ GATE(INFRA_GCPU, infra_gcpu, axi_sel, infra_cg_regs, 6, 0),
+ GATE(INFRA_L2C_SRAM, infra_l2csram, axi_sel, infra_cg_regs, 7, 0),
+ GATE(INFRA_M4U, infra_m4u, axi_sel, infra_cg_regs, 8, 0),
+ GATE(INFRA_CLDMA, infra_cldma, axi_sel, infra_cg_regs, 12, 0),
+ GATE(INFRA_CONNMCU_BUS, infra_connmcubus, axi_sel, infra_cg_regs, 15, 0),
+ GATE(INFRA_KP, infra_kp, axi_sel, infra_cg_regs, 16, 0),
+ GATE(INFRA_APXGPT, infra_apxgpt, axi_sel, infra_cg_regs, 18, 0),
+ GATE(INFRA_SEJ, infra_sej, axi_sel, infra_cg_regs, 19, 0),
+ GATE(INFRA_CCIF0_AP, infra_ccif0ap, axi_sel, infra_cg_regs, 20, 0),
+ GATE(INFRA_CCIF1_AP, infra_ccif1ap, axi_sel, infra_cg_regs, 21, 0),
+ GATE(INFRA_PMIC_SPI, infra_pmicspi, pmicspi_sel, infra_cg_regs, 22, 0),
+ GATE(INFRA_PMIC_WRAP, infra_pmicwrap, axi_sel, infra_cg_regs, 23, 0),
+};
+
+static void __init init_clk_infrasys(void __iomem *infrasys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init infrasys gates:\n");
+ init_clk_gates(infrasys_base, infra_clks, ARRAY_SIZE(infra_clks),
+ clk_data);
+}
+
+static struct mtk_gate_regs peri0_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x0010,
+ .sta_ofs = 0x0018,
+};
+
+static struct mtk_gate peri_clks[] __initdata = {
+ GATE(PERI_DISP_PWM, peri_disp_pwm, disppwm_sel, peri0_cg_regs, 0, 0),
+ GATE(PERI_THERM, peri_therm, axi_sel, peri0_cg_regs, 1, 0),
+ GATE(PERI_PWM1, peri_pwm1, axi_sel, peri0_cg_regs, 2, 0),
+ GATE(PERI_PWM2, peri_pwm2, axi_sel, peri0_cg_regs, 3, 0),
+ GATE(PERI_PWM3, peri_pwm3, axi_sel, peri0_cg_regs, 4, 0),
+ GATE(PERI_PWM4, peri_pwm4, axi_sel, peri0_cg_regs, 5, 0),
+ GATE(PERI_PWM5, peri_pwm5, axi_sel, peri0_cg_regs, 6, 0),
+ GATE(PERI_PWM6, peri_pwm6, axi_sel, peri0_cg_regs, 7, 0),
+ GATE(PERI_PWM7, peri_pwm7, axi_sel, peri0_cg_regs, 8, 0),
+ GATE(PERI_PWM, peri_pwm, axi_sel, peri0_cg_regs, 9, 0),
+ GATE(PERI_USB0, peri_usb0, usb20_sel, peri0_cg_regs, 10, 0),
+ GATE(PERI_IRDA, peri_irda, irda_sel, peri0_cg_regs, 11, 0),
+ GATE(PERI_APDMA, peri_apdma, axi_sel, peri0_cg_regs, 12, 0),
+ GATE(PERI_MSDC30_0, peri_msdc30_0, msdc30_0_sel, peri0_cg_regs, 13, 0),
+ GATE(PERI_MSDC30_1, peri_msdc30_1, msdc30_1_sel, peri0_cg_regs, 14, 0),
+ GATE(PERI_MSDC30_2, peri_msdc30_2, msdc30_2_sel, peri0_cg_regs, 15, 0),
+ GATE(PERI_MSDC30_3, peri_msdc30_3, msdc30_3_sel, peri0_cg_regs, 16, 0),
+ GATE(PERI_UART0, peri_uart0, uart_sel, peri0_cg_regs, 17, 0),
+ GATE(PERI_UART1, peri_uart1, uart_sel, peri0_cg_regs, 18, 0),
+ GATE(PERI_UART2, peri_uart2, uart_sel, peri0_cg_regs, 19, 0),
+ GATE(PERI_UART3, peri_uart3, uart_sel, peri0_cg_regs, 20, 0),
+ GATE(PERI_UART4, peri_uart4, uart_sel, peri0_cg_regs, 21, 0),
+ GATE(PERI_BTIF, peri_btif, axi_sel, peri0_cg_regs, 22, 0),
+ GATE(PERI_I2C0, peri_i2c0, axi_sel, peri0_cg_regs, 23, 0),
+ GATE(PERI_I2C1, peri_i2c1, axi_sel, peri0_cg_regs, 24, 0),
+ GATE(PERI_I2C2, peri_i2c2, axi_sel, peri0_cg_regs, 25, 0),
+ GATE(PERI_I2C3, peri_i2c3, axi_sel, peri0_cg_regs, 26, 0),
+ GATE(PERI_AUXADC, peri_auxadc, axi_sel, peri0_cg_regs, 27, 0),
+ GATE(PERI_SPI0, peri_spi0, spi_sel, peri0_cg_regs, 28, 0),
+ GATE(PERI_IRTX, peri_irtx, irtx_sel, peri0_cg_regs, 29, 0),
+};
+
+static void __init init_clk_perisys(void __iomem *perisys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init perisys gates:\n");
+ init_clk_gates(perisys_base, peri_clks, ARRAY_SIZE(peri_clks),
+ clk_data);
+}
+
+static struct mtk_gate_regs mfg_cg_regs = {
+ .set_ofs = 0x0004,
+ .clr_ofs = 0x0008,
+ .sta_ofs = 0x0000,
+};
+
+static struct mtk_gate mfg_clks[] __initdata = {
+ GATE(MFG_BG3D, mfg_bg3d, mfg_sel, mfg_cg_regs, 0, 0),
+};
+
+static void __init init_clk_mfgsys(void __iomem *mfgsys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init mfgsys gates:\n");
+ init_clk_gates(mfgsys_base, mfg_clks, ARRAY_SIZE(mfg_clks), clk_data);
+}
+
+static struct mtk_gate_regs img_cg_regs = {
+ .set_ofs = 0x0004,
+ .clr_ofs = 0x0008,
+ .sta_ofs = 0x0000,
+};
+
+static struct mtk_gate img_clks[] __initdata = {
+ GATE(IMG_IMAGE_LARB2_SMI, img_image_larb2_smi, mm_sel, img_cg_regs, 0, 0),
+ GATE(IMG_IMAGE_CAM_SMI, img_image_cam_smi, mm_sel, img_cg_regs, 5, 0),
+ GATE(IMG_IMAGE_CAM_CAM, img_image_cam_cam, mm_sel, img_cg_regs, 6, 0),
+ GATE(IMG_IMAGE_SEN_TG, img_image_sen_tg, mm_sel, img_cg_regs, 7, 0),
+ GATE(IMG_IMAGE_SEN_CAM, img_image_sen_cam, camtg_sel, img_cg_regs, 8, 0),
+ GATE(IMG_IMAGE_CAM_SV, img_image_cam_sv, mm_sel, img_cg_regs, 9, 0),
+ GATE(IMG_IMAGE_SUFOD, img_image_sufod, mm_sel, img_cg_regs, 10, 0),
+ GATE(IMG_IMAGE_FD, img_image_fd, mm_sel, img_cg_regs, 11, 0),
+};
+
+static void __init init_clk_imgsys(void __iomem *imgsys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init imgsys gates:\n");
+ init_clk_gates(imgsys_base, img_clks, ARRAY_SIZE(img_clks), clk_data);
+}
+
+static struct mtk_gate_regs mm0_cg_regs = {
+ .set_ofs = 0x0104,
+ .clr_ofs = 0x0108,
+ .sta_ofs = 0x0100,
+};
+
+static struct mtk_gate_regs mm1_cg_regs = {
+ .set_ofs = 0x0114,
+ .clr_ofs = 0x0118,
+ .sta_ofs = 0x0110,
+};
+
+static struct mtk_gate mm_clks[] __initdata = {
+ /* MM0 */
+ GATE(MM_DISP0_SMI_COMMON, mm_disp0_smi_common, mm_sel, mm0_cg_regs, 0, 0),
+ GATE(MM_DISP0_SMI_LARB0, mm_disp0_smi_larb0, mm_sel, mm0_cg_regs, 1, 0),
+ GATE(MM_DISP0_CAM_MDP, mm_disp0_cam_mdp, mm_sel, mm0_cg_regs, 2, 0),
+ GATE(MM_DISP0_MDP_RDMA, mm_disp0_mdp_rdma, mm_sel, mm0_cg_regs, 3, 0),
+ GATE(MM_DISP0_MDP_RSZ0, mm_disp0_mdp_rsz0, mm_sel, mm0_cg_regs, 4, 0),
+ GATE(MM_DISP0_MDP_RSZ1, mm_disp0_mdp_rsz1, mm_sel, mm0_cg_regs, 5, 0),
+ GATE(MM_DISP0_MDP_TDSHP, mm_disp0_mdp_tdshp, mm_sel, mm0_cg_regs, 6, 0),
+ GATE(MM_DISP0_MDP_WDMA, mm_disp0_mdp_wdma, mm_sel, mm0_cg_regs, 7, 0),
+ GATE(MM_DISP0_MDP_WROT, mm_disp0_mdp_wrot, mm_sel, mm0_cg_regs, 8, 0),
+ GATE(MM_DISP0_FAKE_ENG, mm_disp0_fake_eng, mm_sel, mm0_cg_regs, 9, 0),
+ GATE(MM_DISP0_DISP_OVL0, mm_disp0_disp_ovl0, mm_sel, mm0_cg_regs, 10, 0),
+ GATE(MM_DISP0_DISP_RDMA0, mm_disp0_disp_rdma0, mm_sel, mm0_cg_regs, 11, 0),
+ GATE(MM_DISP0_DISP_RDMA1, mm_disp0_disp_rdma1, mm_sel, mm0_cg_regs, 12, 0),
+ GATE(MM_DISP0_DISP_WDMA0, mm_disp0_disp_wdma0, mm_sel, mm0_cg_regs, 13, 0),
+ GATE(MM_DISP0_DISP_COLOR, mm_disp0_disp_color, mm_sel, mm0_cg_regs, 14, 0),
+ GATE(MM_DISP0_DISP_CCORR, mm_disp0_disp_ccorr, mm_sel, mm0_cg_regs, 15, 0),
+ GATE(MM_DISP0_DISP_AAL, mm_disp0_disp_aal, mm_sel, mm0_cg_regs, 16, 0),
+ GATE(MM_DISP0_DISP_GAMMA, mm_disp0_disp_gamma, mm_sel, mm0_cg_regs, 17, 0),
+ GATE(MM_DISP0_DISP_DITHER, mm_disp0_disp_dither, mm_sel, mm0_cg_regs, 18, 0),
+ /* MM1 */
+ GATE(MM_DISP1_DSI_ENGINE, mm_disp1_dsi_engine, mm_sel, mm1_cg_regs, 2, 0),
+ GATE(MM_DISP1_DSI_DIGITAL, mm_disp1_dsi_digital, mm_sel, mm1_cg_regs, 3, 0),
+ GATE(MM_DISP1_DPI_ENGINE, mm_disp1_dpi_engine, mm_sel, mm1_cg_regs, 4, 0),
+ GATE(MM_DISP1_DPI_PIXEL, mm_disp1_dpi_pixel, dpi0_sel, mm1_cg_regs, 5, 0),
+};
+
+static void __init init_clk_mmsys(void __iomem *mmsys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init mmsys gates:\n");
+ init_clk_gates(mmsys_base, mm_clks, ARRAY_SIZE(mm_clks),
+ clk_data);
+}
+
+static struct mtk_gate_regs vdec0_cg_regs = {
+ .set_ofs = 0x0000,
+ .clr_ofs = 0x0004,
+ .sta_ofs = 0x0000,
+};
+
+static struct mtk_gate_regs vdec1_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x000c,
+ .sta_ofs = 0x0008,
+};
+
+static struct mtk_gate vdec_clks[] __initdata = {
+ GATE(VDEC0_VDEC, vdec0_vdec, vdec_sel, vdec0_cg_regs, 0, CLK_GATE_INVERSE),
+ GATE(VDEC1_LARB, vdec1_larb, vdec_sel, vdec1_cg_regs, 0, CLK_GATE_INVERSE),
+};
+
+static void __init init_clk_vdecsys(void __iomem *vdecsys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init vdecsys gates:\n");
+ init_clk_gates(vdecsys_base, vdec_clks, ARRAY_SIZE(vdec_clks),
+ clk_data);
+}
+
+static struct mtk_gate_regs venc_cg_regs = {
+ .set_ofs = 0x0004,
+ .clr_ofs = 0x0008,
+ .sta_ofs = 0x0000,
+};
+
+static struct mtk_gate venc_clks[] __initdata = {
+ GATE(VENC_LARB, venc_larb, mm_sel, venc_cg_regs, 0, CLK_GATE_INVERSE),
+ GATE(VENC_VENC, venc_venc, mm_sel, venc_cg_regs, 4, CLK_GATE_INVERSE),
+ GATE(VENC_JPGENC, venc_jpgenc, mm_sel, venc_cg_regs, 8, CLK_GATE_INVERSE),
+ GATE(VENC_JPGDEC, venc_jpgdec, mm_sel, venc_cg_regs, 12, CLK_GATE_INVERSE),
+};
+
+static void __init init_clk_vencsys(void __iomem *vencsys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init vencsys gates:\n");
+ init_clk_gates(vencsys_base, venc_clks, ARRAY_SIZE(venc_clks),
+ clk_data);
+}
+
+static struct mtk_gate_regs aud_cg_regs = {
+ .set_ofs = 0x0000,
+ .clr_ofs = 0x0000,
+ .sta_ofs = 0x0000,
+};
+
+static struct mtk_gate audio_clks[] __initdata = {
+ GATE(AUDIO_AFE, audio_afe, aud_intbus_sel, aud_cg_regs, 2, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_I2S, audio_i2s, aud_intbus_sel, aud_cg_regs, 6, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_22M, audio_22m, aud_intbus_sel, aud_cg_regs, 8, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_24M, audio_24m, aud_intbus_sel, aud_cg_regs, 9, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_APLL2_TUNER, audio_apll2_tuner, aud_intbus_sel, aud_cg_regs, 18, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_APLL_TUNER, audio_apll_tuner, aud_intbus_sel, aud_cg_regs, 19, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_ADC, audio_adc, aud_intbus_sel, aud_cg_regs, 24, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_DAC, audio_dac, aud_intbus_sel, aud_cg_regs, 25, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_DAC_PREDIS, audio_dac_predis, aud_intbus_sel, aud_cg_regs, 26, CLK_GATE_NO_SETCLR_REG),
+ GATE(AUDIO_TML, audio_tml, aud_intbus_sel, aud_cg_regs, 27, CLK_GATE_NO_SETCLR_REG),
+};
+
+static void __init init_clk_audiosys(void __iomem *audiosys_base,
+ struct clk_onecell_data *clk_data)
+{
+ pr_debug("init audiosys gates:\n");
+ init_clk_gates(audiosys_base, audio_clks, ARRAY_SIZE(audio_clks),
+ clk_data);
+}
+
+/*
+ * device tree support
+ */
+
+static struct clk_onecell_data *alloc_clk_data(unsigned int clk_num)
+{
+ int i;
+ struct clk_onecell_data *clk_data;
+
+ clk_data = kzalloc(sizeof(clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return NULL;
+
+ clk_data->clks = kcalloc(clk_num, sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return NULL;
+ }
+
+ clk_data->clk_num = clk_num;
+
+ for (i = 0; i < clk_num; ++i)
+ clk_data->clks[i] = ERR_PTR(-ENOENT);
+
+ return clk_data;
+}
+
+static void __iomem *get_reg(struct device_node *np, int index)
+{
+#if DUMMY_REG_TEST
+ return kzalloc(PAGE_SIZE, GFP_KERNEL);
+#else
+ return of_iomap(np, index);
+#endif
+}
+
+static void __init mt_topckgen_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap topckgen failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(TOP_NR_CLK);
+
+ init_clk_root_alias(clk_data);
+ init_clk_top_div(clk_data);
+ init_clk_topckgen(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt6735-topckgen", mt_topckgen_init);
+
+static void __init mt_apmixedsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap apmixedsys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(APMIXED_NR_CLK);
+
+ init_clk_apmixedsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt6735-apmixedsys",
+ mt_apmixedsys_init);
+
+static void __init mt_infrasys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap infrasys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(INFRA_NR_CLK);
+
+ init_clk_infrasys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt6735-infrasys", mt_infrasys_init);
+
+static void __init mt_perisys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap perisys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(PERI_NR_CLK);
+
+ init_clk_perisys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_perisys, "mediatek,mt6735-perisys", mt_perisys_init);
+
+static void __init mt_mfgsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap mfgsys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(MFG_NR_CLK);
+
+ init_clk_mfgsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_mfgsys, "mediatek,mt6735-mfgsys", mt_mfgsys_init);
+
+static void __init mt_imgsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap imgsys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(IMG_NR_CLK);
+
+ init_clk_imgsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_imgsys, "mediatek,mt6735-imgsys", mt_imgsys_init);
+
+static void __init mt_mmsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap mmsys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(MM_NR_CLK);
+
+ init_clk_mmsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_mmsys, "mediatek,mt6735-mmsys", mt_mmsys_init);
+
+static void __init mt_vdecsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap vdecsys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(VDEC_NR_CLK);
+
+ init_clk_vdecsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_vdecsys, "mediatek,mt6735-vdecsys", mt_vdecsys_init);
+
+static void __init mt_vencsys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap vencsys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(VENC_NR_CLK);
+
+ init_clk_vencsys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_vencsys, "mediatek,mt6735-vencsys", mt_vencsys_init);
+
+static void __init mt_audiosys_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ void __iomem *base;
+ int r;
+
+ pr_debug("%s: %s\n", __func__, node->name);
+
+ base = get_reg(node, 0);
+ if (!base) {
+ pr_err("ioremap audiosys failed\n");
+ return;
+ }
+
+ clk_data = alloc_clk_data(AUDIO_NR_CLK);
+
+ init_clk_audiosys(base, clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r)
+ pr_err("could not register clock provide\n");
+}
+CLK_OF_DECLARE(mtk_audiosys, "mediatek,mt6735-audiosys", mt_audiosys_init);
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
new file mode 100644
index 000000000..60416691e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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/of.h>
+#include <linux/of_address.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+
+#ifdef Bring_Up
+#ifdef pr_debug
+#undef pr_debug
+#define pr_debug printk
+#endif
+#endif /* Bring_Up */
+
+static DEFINE_SPINLOCK(clk_ops_lock);
+
+spinlock_t *get_mtk_clk_lock(void)
+{
+ return &clk_ops_lock;
+}
+
+/*
+ * clk_mux
+ */
+
+struct clk *mtk_clk_register_mux(
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ void __iomem *base_addr,
+ u8 shift,
+ u8 width,
+ u8 gate_bit)
+{
+ struct clk *clk;
+ struct clk_mux *mux;
+ struct clk_gate *gate = NULL;
+ struct clk_hw *gate_hw = NULL;
+ const struct clk_ops *gate_ops = NULL;
+ u32 mask = BIT(width) - 1;
+
+ pr_debug("name: %s, num_parents: %d, gate_bit: %d\n",
+ name, (int)num_parents, (int)gate_bit);
+
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->reg = base_addr;
+ mux->mask = mask;
+ mux->shift = shift;
+ mux->flags = 0;
+ mux->lock = &clk_ops_lock;
+
+ if (gate_bit <= MAX_MUX_GATE_BIT) {
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(mux);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ gate->reg = base_addr;
+ gate->bit_idx = gate_bit;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->lock = &clk_ops_lock;
+
+ gate_hw = &gate->hw;
+ gate_ops = &clk_gate_ops;
+ }
+
+ clk = clk_register_composite(NULL, name, parent_names, num_parents,
+ &mux->hw, &clk_mux_ops,
+ NULL, NULL,
+ gate_hw, gate_ops,
+ CLK_IGNORE_UNUSED);
+
+ if (IS_ERR(clk)) {
+ kfree(gate);
+ kfree(mux);
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
new file mode 100644
index 000000000..17b429876
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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 __DRV_CLK_MTK_H
+#define __DRV_CLK_MTK_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#define CLK_DEBUG 0
+#define DUMMY_REG_TEST 0
+#if !defined(CONFIG_MTK_LEGACY)
+#define Bring_Up /* FIXME: disable after CCF is ready */
+#endif /* !defined(CONFIG_MTK_LEGACY) */
+
+extern spinlock_t *get_mtk_clk_lock(void);
+
+#define mtk_clk_lock(flags) spin_lock_irqsave(get_mtk_clk_lock(), flags)
+#define mtk_clk_unlock(flags) \
+ spin_unlock_irqrestore(get_mtk_clk_lock(), flags)
+
+#define MAX_MUX_GATE_BIT 31
+#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1)
+
+struct clk *mtk_clk_register_mux(
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ void __iomem *base_addr,
+ u8 shift,
+ u8 width,
+ u8 gate_bit);
+
+#endif /* __DRV_CLK_MTK_H */
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
new file mode 100644
index 000000000..864cc6003
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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/io.h>
+#include <linux/slab.h>
+#include <linux/clkdev.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+/*
+ * clk_pll
+ */
+
+struct clk *mtk_clk_register_pll(
+ const char *name,
+ const char *parent_name,
+ u32 *base_addr,
+ u32 *pwr_addr,
+ u32 en_mask,
+ u32 flags,
+ const struct clk_ops *ops)
+{
+ struct mtk_clk_pll *pll;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ pr_debug("name: %s\n", name);
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->base_addr = base_addr;
+ pll->pwr_addr = pwr_addr;
+ pll->en_mask = en_mask;
+ pll->flags = flags;
+ pll->hw.init = &init;
+
+ init.name = name;
+ init.ops = ops;
+ init.flags = CLK_IGNORE_UNUSED;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &pll->hw);
+
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h
new file mode 100644
index 000000000..cb7f33542
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pll.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: James Liao <jamesjj.liao@mediatek.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.
+ *
+ * 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 __DRV_CLK_PLL_H
+#define __DRV_CLK_PLL_H
+
+/*
+ * This is a private header file. DO NOT include it except clk-*.c.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+struct mtk_clk_pll {
+ struct clk_hw hw;
+ void __iomem *base_addr;
+ void __iomem *pwr_addr;
+ u32 en_mask;
+ u32 flags;
+};
+
+#define to_mtk_clk_pll(_hw) container_of(_hw, struct mtk_clk_pll, hw)
+
+#define HAVE_RST_BAR BIT(0)
+#define HAVE_PLL_HP BIT(1)
+#define HAVE_FIX_FRQ BIT(2)
+#define PLL_AO BIT(3)
+
+struct clk *mtk_clk_register_pll(
+ const char *name,
+ const char *parent_name,
+ u32 *base_addr,
+ u32 *pwr_addr,
+ u32 en_mask,
+ u32 flags,
+ const struct clk_ops *ops);
+
+#endif /* __DRV_CLK_PLL_H */
diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile
new file mode 100644
index 000000000..392d78044
--- /dev/null
+++ b/drivers/clk/mmp/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for mmp specific clk
+#
+
+obj-y += clk-apbc.o clk-apmu.o clk-frac.o
+
+obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
+obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
+obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o
diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c
new file mode 100644
index 000000000..d14120eaa
--- /dev/null
+++ b/drivers/clk/mmp/clk-apbc.c
@@ -0,0 +1,152 @@
+/*
+ * mmp APB clock operation source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+/* Common APB clock register bit definitions */
+#define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */
+#define APBC_FNCLK (1 << 1) /* Functional Clock Enable */
+#define APBC_RST (1 << 2) /* Reset Generation */
+#define APBC_POWER (1 << 7) /* Reset Generation */
+
+#define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw)
+struct clk_apbc {
+ struct clk_hw hw;
+ void __iomem *base;
+ unsigned int delay;
+ unsigned int flags;
+ spinlock_t *lock;
+};
+
+static int clk_apbc_prepare(struct clk_hw *hw)
+{
+ struct clk_apbc *apbc = to_clk_apbc(hw);
+ unsigned int data;
+ unsigned long flags = 0;
+
+ /*
+ * It may share same register as MUX clock,
+ * and it will impact FNCLK enable. Spinlock is needed
+ */
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ if (apbc->flags & APBC_POWER_CTRL)
+ data |= APBC_POWER;
+ data |= APBC_FNCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+
+ udelay(apbc->delay);
+
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ data |= APBC_APBCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+
+ udelay(apbc->delay);
+
+ if (!(apbc->flags & APBC_NO_BUS_CTRL)) {
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ data &= ~APBC_RST;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+ }
+
+ return 0;
+}
+
+static void clk_apbc_unprepare(struct clk_hw *hw)
+{
+ struct clk_apbc *apbc = to_clk_apbc(hw);
+ unsigned long data;
+ unsigned long flags = 0;
+
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ if (apbc->flags & APBC_POWER_CTRL)
+ data &= ~APBC_POWER;
+ data &= ~APBC_FNCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+
+ udelay(10);
+
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ data &= ~APBC_APBCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+}
+
+struct clk_ops clk_apbc_ops = {
+ .prepare = clk_apbc_prepare,
+ .unprepare = clk_apbc_unprepare,
+};
+
+struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name,
+ void __iomem *base, unsigned int delay,
+ unsigned int apbc_flags, spinlock_t *lock)
+{
+ struct clk_apbc *apbc;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ apbc = kzalloc(sizeof(*apbc), GFP_KERNEL);
+ if (!apbc)
+ return NULL;
+
+ init.name = name;
+ init.ops = &clk_apbc_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ apbc->base = base;
+ apbc->delay = delay;
+ apbc->flags = apbc_flags;
+ apbc->lock = lock;
+ apbc->hw.init = &init;
+
+ clk = clk_register(NULL, &apbc->hw);
+ if (IS_ERR(clk))
+ kfree(apbc);
+
+ return clk;
+}
diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c
new file mode 100644
index 000000000..abe182b23
--- /dev/null
+++ b/drivers/clk/mmp/clk-apmu.c
@@ -0,0 +1,97 @@
+/*
+ * mmp AXI peripharal clock operation source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define to_clk_apmu(clk) (container_of(clk, struct clk_apmu, clk))
+struct clk_apmu {
+ struct clk_hw hw;
+ void __iomem *base;
+ u32 rst_mask;
+ u32 enable_mask;
+ spinlock_t *lock;
+};
+
+static int clk_apmu_enable(struct clk_hw *hw)
+{
+ struct clk_apmu *apmu = to_clk_apmu(hw);
+ unsigned long data;
+ unsigned long flags = 0;
+
+ if (apmu->lock)
+ spin_lock_irqsave(apmu->lock, flags);
+
+ data = readl_relaxed(apmu->base) | apmu->enable_mask;
+ writel_relaxed(data, apmu->base);
+
+ if (apmu->lock)
+ spin_unlock_irqrestore(apmu->lock, flags);
+
+ return 0;
+}
+
+static void clk_apmu_disable(struct clk_hw *hw)
+{
+ struct clk_apmu *apmu = to_clk_apmu(hw);
+ unsigned long data;
+ unsigned long flags = 0;
+
+ if (apmu->lock)
+ spin_lock_irqsave(apmu->lock, flags);
+
+ data = readl_relaxed(apmu->base) & ~apmu->enable_mask;
+ writel_relaxed(data, apmu->base);
+
+ if (apmu->lock)
+ spin_unlock_irqrestore(apmu->lock, flags);
+}
+
+struct clk_ops clk_apmu_ops = {
+ .enable = clk_apmu_enable,
+ .disable = clk_apmu_disable,
+};
+
+struct clk *mmp_clk_register_apmu(const char *name, const char *parent_name,
+ void __iomem *base, u32 enable_mask, spinlock_t *lock)
+{
+ struct clk_apmu *apmu;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ apmu = kzalloc(sizeof(*apmu), GFP_KERNEL);
+ if (!apmu)
+ return NULL;
+
+ init.name = name;
+ init.ops = &clk_apmu_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ apmu->base = base;
+ apmu->enable_mask = enable_mask;
+ apmu->lock = lock;
+ apmu->hw.init = &init;
+
+ clk = clk_register(NULL, &apmu->hw);
+
+ if (IS_ERR(clk))
+ kfree(apmu);
+
+ return clk;
+}
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c
new file mode 100644
index 000000000..80c1dd15d
--- /dev/null
+++ b/drivers/clk/mmp/clk-frac.c
@@ -0,0 +1,153 @@
+/*
+ * mmp factor clock operation source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "clk.h"
+/*
+ * It is M/N clock
+ *
+ * Fout from synthesizer can be given from two equations:
+ * numerator/denominator = Fin / (Fout * factor)
+ */
+
+#define to_clk_factor(hw) container_of(hw, struct clk_factor, hw)
+struct clk_factor {
+ struct clk_hw hw;
+ void __iomem *base;
+ struct clk_factor_masks *masks;
+ struct clk_factor_tbl *ftbl;
+ unsigned int ftbl_cnt;
+};
+
+static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ struct clk_factor *factor = to_clk_factor(hw);
+ unsigned long rate = 0, prev_rate;
+ int i;
+
+ for (i = 0; i < factor->ftbl_cnt; i++) {
+ prev_rate = rate;
+ rate = (((*prate / 10000) * factor->ftbl[i].num) /
+ (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ if (rate > drate)
+ break;
+ }
+ if (i == 0)
+ return rate;
+ else
+ return prev_rate;
+}
+
+static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_factor *factor = to_clk_factor(hw);
+ struct clk_factor_masks *masks = factor->masks;
+ unsigned int val, num, den;
+
+ val = readl_relaxed(factor->base);
+
+ /* calculate numerator */
+ num = (val >> masks->num_shift) & masks->num_mask;
+
+ /* calculate denominator */
+ den = (val >> masks->den_shift) & masks->num_mask;
+
+ if (!den)
+ return 0;
+
+ return (((parent_rate / 10000) * den) /
+ (num * factor->masks->factor)) * 10000;
+}
+
+/* Configures new clock rate*/
+static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_factor *factor = to_clk_factor(hw);
+ struct clk_factor_masks *masks = factor->masks;
+ int i;
+ unsigned long val;
+ unsigned long prev_rate, rate = 0;
+
+ for (i = 0; i < factor->ftbl_cnt; i++) {
+ prev_rate = rate;
+ rate = (((prate / 10000) * factor->ftbl[i].num) /
+ (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ if (rate > drate)
+ break;
+ }
+ if (i > 0)
+ i--;
+
+ val = readl_relaxed(factor->base);
+
+ val &= ~(masks->num_mask << masks->num_shift);
+ val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift;
+
+ val &= ~(masks->den_mask << masks->den_shift);
+ val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift;
+
+ writel_relaxed(val, factor->base);
+
+ return 0;
+}
+
+static struct clk_ops clk_factor_ops = {
+ .recalc_rate = clk_factor_recalc_rate,
+ .round_rate = clk_factor_round_rate,
+ .set_rate = clk_factor_set_rate,
+};
+
+struct clk *mmp_clk_register_factor(const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *base,
+ struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl,
+ unsigned int ftbl_cnt)
+{
+ struct clk_factor *factor;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ if (!masks) {
+ pr_err("%s: must pass a clk_factor_mask\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ factor = kzalloc(sizeof(*factor), GFP_KERNEL);
+ if (!factor) {
+ pr_err("%s: could not allocate factor clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_aux assignments */
+ factor->base = base;
+ factor->masks = masks;
+ factor->ftbl = ftbl;
+ factor->ftbl_cnt = ftbl_cnt;
+ factor->hw.init = &init;
+
+ init.name = name;
+ init.ops = &clk_factor_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &factor->hw);
+ if (IS_ERR_OR_NULL(clk))
+ kfree(factor);
+
+ return clk;
+}
diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c
new file mode 100644
index 000000000..d1f1a19d4
--- /dev/null
+++ b/drivers/clk/mmp/clk-mmp2.c
@@ -0,0 +1,449 @@
+/*
+ * mmp2 clock framework source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/addr-map.h>
+
+#include "clk.h"
+
+#define APBC_RTC 0x0
+#define APBC_TWSI0 0x4
+#define APBC_TWSI1 0x8
+#define APBC_TWSI2 0xc
+#define APBC_TWSI3 0x10
+#define APBC_TWSI4 0x7c
+#define APBC_TWSI5 0x80
+#define APBC_KPC 0x18
+#define APBC_UART0 0x2c
+#define APBC_UART1 0x30
+#define APBC_UART2 0x34
+#define APBC_UART3 0x88
+#define APBC_GPIO 0x38
+#define APBC_PWM0 0x3c
+#define APBC_PWM1 0x40
+#define APBC_PWM2 0x44
+#define APBC_PWM3 0x48
+#define APBC_SSP0 0x50
+#define APBC_SSP1 0x54
+#define APBC_SSP2 0x58
+#define APBC_SSP3 0x5c
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_SDH2 0xe8
+#define APMU_SDH3 0xec
+#define APMU_USB 0x5c
+#define APMU_DISP0 0x4c
+#define APMU_DISP1 0x110
+#define APMU_CCIC0 0x50
+#define APMU_CCIC1 0xf4
+#define MPMU_UART_PLL 0x14
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = 0x1fff,
+ .den_mask = 0x1fff,
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static struct clk_factor_tbl uart_factor_tbl[] = {
+ {.num = 14634, .den = 2165}, /*14.745MHZ */
+ {.num = 3521, .den = 689}, /*19.23MHZ */
+ {.num = 9679, .den = 5728}, /*58.9824MHZ */
+ {.num = 15850, .den = 9451}, /*59.429MHZ */
+};
+
+static const char *uart_parent[] = {"uart_pll", "vctcxo"};
+static const char *ssp_parent[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
+static const char *sdh_parent[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
+static const char *disp_parent[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
+static const char *ccic_parent[] = {"pll1_2", "pll1_16", "vctcxo"};
+
+void __init mmp2_clk_init(void)
+{
+ struct clk *clk;
+ struct clk *vctcxo;
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbc_base;
+
+ mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
+ if (mpmu_base == NULL) {
+ pr_err("error to ioremap MPMU base\n");
+ return;
+ }
+
+ apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
+ if (apmu_base == NULL) {
+ pr_err("error to ioremap APMU base\n");
+ return;
+ }
+
+ apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K);
+ if (apbc_base == NULL) {
+ pr_err("error to ioremap APBC base\n");
+ return;
+ }
+
+ clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200);
+ clk_register_clkdev(clk, "clk32", NULL);
+
+ vctcxo = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk_register_clkdev(vctcxo, "vctcxo", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT,
+ 800000000);
+ clk_register_clkdev(clk, "pll1", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "usb_pll", NULL, CLK_IS_ROOT,
+ 480000000);
+ clk_register_clkdev(clk, "usb_pll", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll2", NULL, CLK_IS_ROOT,
+ 960000000);
+ clk_register_clkdev(clk, "pll2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_20", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 5);
+ clk_register_clkdev(clk, "pll1_20", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_3", "pll1",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll1_3", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_3",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_2", "pll2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_4", "pll2_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_8", "pll2_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_16", "pll2_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_3", "pll2",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll2_3", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_6", "pll2_3",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_12", "pll2_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vctcxo_2", "vctcxo",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "vctcxo_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vctcxo_4", "vctcxo_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "vctcxo_4", NULL);
+
+ clk = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
+ mpmu_base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl));
+ clk_set_rate(clk, 14745600);
+ clk_register_clkdev(clk, "uart_pll", NULL);
+
+ clk = mmp_clk_register_apbc("twsi0", "vctcxo",
+ apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
+
+ clk = mmp_clk_register_apbc("twsi1", "vctcxo",
+ apbc_base + APBC_TWSI1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
+
+ clk = mmp_clk_register_apbc("twsi2", "vctcxo",
+ apbc_base + APBC_TWSI2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.2");
+
+ clk = mmp_clk_register_apbc("twsi3", "vctcxo",
+ apbc_base + APBC_TWSI3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.3");
+
+ clk = mmp_clk_register_apbc("twsi4", "vctcxo",
+ apbc_base + APBC_TWSI4, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.4");
+
+ clk = mmp_clk_register_apbc("twsi5", "vctcxo",
+ apbc_base + APBC_TWSI5, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.5");
+
+ clk = mmp_clk_register_apbc("gpio", "vctcxo",
+ apbc_base + APBC_GPIO, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-gpio");
+
+ clk = mmp_clk_register_apbc("kpc", "clk32",
+ apbc_base + APBC_KPC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa27x-keypad");
+
+ clk = mmp_clk_register_apbc("rtc", "clk32",
+ apbc_base + APBC_RTC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-rtc");
+
+ clk = mmp_clk_register_apbc("pwm0", "vctcxo",
+ apbc_base + APBC_PWM0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.0");
+
+ clk = mmp_clk_register_apbc("pwm1", "vctcxo",
+ apbc_base + APBC_PWM1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.1");
+
+ clk = mmp_clk_register_apbc("pwm2", "vctcxo",
+ apbc_base + APBC_PWM2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.2");
+
+ clk = mmp_clk_register_apbc("pwm3", "vctcxo",
+ apbc_base + APBC_PWM3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.3");
+
+ clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("uart0", "uart0_mux",
+ apbc_base + APBC_UART0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
+
+ clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("uart1", "uart1_mux",
+ apbc_base + APBC_UART1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
+
+ clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("uart2", "uart2_mux",
+ apbc_base + APBC_UART2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
+
+ clk = clk_register_mux(NULL, "uart3_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART3, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.3", NULL);
+
+ clk = mmp_clk_register_apbc("uart3", "uart3_mux",
+ apbc_base + APBC_UART3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.3");
+
+ clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("ssp0", "ssp0_mux",
+ apbc_base + APBC_SSP0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.0");
+
+ clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("ssp1", "ssp1_mux",
+ apbc_base + APBC_SSP1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.1");
+
+ clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("ssp2", "ssp2_mux",
+ apbc_base + APBC_SSP2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.2");
+
+ clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.3", NULL);
+
+ clk = mmp_clk_register_apbc("ssp3", "ssp3_mux",
+ apbc_base + APBC_SSP3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.3");
+
+ clk = clk_register_mux(NULL, "sdh_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh_mux", NULL);
+
+ clk = clk_register_divider(NULL, "sdh_div", "sdh_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_SDH0,
+ 10, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "sdh_div", NULL);
+
+ clk = mmp_clk_register_apmu("sdh0", "sdh_div", apmu_base + APMU_SDH0,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.0");
+
+ clk = mmp_clk_register_apmu("sdh1", "sdh_div", apmu_base + APMU_SDH1,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.1");
+
+ clk = mmp_clk_register_apmu("sdh2", "sdh_div", apmu_base + APMU_SDH2,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.2");
+
+ clk = mmp_clk_register_apmu("sdh3", "sdh_div", apmu_base + APMU_SDH3,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.3");
+
+ clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB,
+ 0x9, &clk_lock);
+ clk_register_clkdev(clk, "usb_clk", NULL);
+
+ clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.0", NULL);
+
+ clk = clk_register_divider(NULL, "disp0_div", "disp0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_DISP0,
+ 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "disp_div.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0", "disp0_div",
+ apmu_base + APMU_DISP0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-disp.0");
+
+ clk = clk_register_divider(NULL, "disp0_sphy_div", "disp0_mux", 0,
+ apmu_base + APMU_DISP0, 15, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_sphy_div.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0_sphy", "disp0_sphy_div",
+ apmu_base + APMU_DISP0, 0x1024, &clk_lock);
+ clk_register_clkdev(clk, "disp_sphy.0", NULL);
+
+ clk = clk_register_mux(NULL, "disp1_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.1", NULL);
+
+ clk = clk_register_divider(NULL, "disp1_div", "disp1_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_DISP1,
+ 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "disp_div.1", NULL);
+
+ clk = mmp_clk_register_apmu("disp1", "disp1_div",
+ apmu_base + APMU_DISP1, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-disp.1");
+
+ clk = mmp_clk_register_apmu("ccic_arbiter", "vctcxo",
+ apmu_base + APMU_CCIC0, 0x1800, &clk_lock);
+ clk_register_clkdev(clk, "ccic_arbiter", NULL);
+
+ clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.0", NULL);
+
+ clk = clk_register_divider(NULL, "ccic0_div", "ccic0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 17, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "ccic_div.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0", "ccic0_div",
+ apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
+
+ clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_div",
+ apmu_base + APMU_CCIC0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
+
+ clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_div",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.0");
+
+ clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
+ apmu_base + APMU_CCIC0, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
+
+ clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.1", NULL);
+
+ clk = clk_register_divider(NULL, "ccic1_div", "ccic1_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1,
+ 16, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "ccic_div.1", NULL);
+
+ clk = mmp_clk_register_apmu("ccic1", "ccic1_div",
+ apmu_base + APMU_CCIC1, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.1");
+
+ clk = mmp_clk_register_apmu("ccic1_phy", "ccic1_div",
+ apmu_base + APMU_CCIC1, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.1");
+
+ clk = clk_register_divider(NULL, "ccic1_sphy_div", "ccic1_div",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.1");
+
+ clk = mmp_clk_register_apmu("ccic1_sphy", "ccic1_sphy_div",
+ apmu_base + APMU_CCIC1, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.1");
+}
diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c
new file mode 100644
index 000000000..28b3b51c7
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa168.c
@@ -0,0 +1,346 @@
+/*
+ * pxa168 clock framework source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/addr-map.h>
+
+#include "clk.h"
+
+#define APBC_RTC 0x28
+#define APBC_TWSI0 0x2c
+#define APBC_KPC 0x30
+#define APBC_UART0 0x0
+#define APBC_UART1 0x4
+#define APBC_GPIO 0x8
+#define APBC_PWM0 0xc
+#define APBC_PWM1 0x10
+#define APBC_PWM2 0x14
+#define APBC_PWM3 0x18
+#define APBC_SSP0 0x81c
+#define APBC_SSP1 0x820
+#define APBC_SSP2 0x84c
+#define APBC_SSP3 0x858
+#define APBC_SSP4 0x85c
+#define APBC_TWSI1 0x6c
+#define APBC_UART2 0x70
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_USB 0x5c
+#define APMU_DISP0 0x4c
+#define APMU_CCIC0 0x50
+#define APMU_DFC 0x60
+#define MPMU_UART_PLL 0x14
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = 0x1fff,
+ .den_mask = 0x1fff,
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static struct clk_factor_tbl uart_factor_tbl[] = {
+ {.num = 8125, .den = 1536}, /*14.745MHZ */
+};
+
+static const char *uart_parent[] = {"pll1_3_16", "uart_pll"};
+static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
+static const char *sdh_parent[] = {"pll1_12", "pll1_13"};
+static const char *disp_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"};
+
+void __init pxa168_clk_init(void)
+{
+ struct clk *clk;
+ struct clk *uart_pll;
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbc_base;
+
+ mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
+ if (mpmu_base == NULL) {
+ pr_err("error to ioremap MPMU base\n");
+ return;
+ }
+
+ apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
+ if (apmu_base == NULL) {
+ pr_err("error to ioremap APMU base\n");
+ return;
+ }
+
+ apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K);
+ if (apbc_base == NULL) {
+ pr_err("error to ioremap APBC base\n");
+ return;
+ }
+
+ clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200);
+ clk_register_clkdev(clk, "clk32", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk_register_clkdev(clk, "vctcxo", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT,
+ 624000000);
+ clk_register_clkdev(clk, "pll1", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll1_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_24", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_48", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_96", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1",
+ CLK_SET_RATE_PARENT, 1, 13);
+ clk_register_clkdev(clk, "pll1_13", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_13_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_2_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1",
+ CLK_SET_RATE_PARENT, 3, 16);
+ clk_register_clkdev(clk, "pll1_3_16", NULL);
+
+ uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
+ mpmu_base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl));
+ clk_set_rate(uart_pll, 14745600);
+ clk_register_clkdev(uart_pll, "uart_pll", NULL);
+
+ clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5",
+ apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
+
+ clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5",
+ apbc_base + APBC_TWSI1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
+
+ clk = mmp_clk_register_apbc("gpio", "vctcxo",
+ apbc_base + APBC_GPIO, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-gpio");
+
+ clk = mmp_clk_register_apbc("kpc", "clk32",
+ apbc_base + APBC_KPC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa27x-keypad");
+
+ clk = mmp_clk_register_apbc("rtc", "clk32",
+ apbc_base + APBC_RTC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sa1100-rtc");
+
+ clk = mmp_clk_register_apbc("pwm0", "pll1_48",
+ apbc_base + APBC_PWM0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.0");
+
+ clk = mmp_clk_register_apbc("pwm1", "pll1_48",
+ apbc_base + APBC_PWM1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.1");
+
+ clk = mmp_clk_register_apbc("pwm2", "pll1_48",
+ apbc_base + APBC_PWM2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.2");
+
+ clk = mmp_clk_register_apbc("pwm3", "pll1_48",
+ apbc_base + APBC_PWM3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.3");
+
+ clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("uart0", "uart0_mux",
+ apbc_base + APBC_UART0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
+
+ clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("uart1", "uart1_mux",
+ apbc_base + APBC_UART1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
+
+ clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("uart2", "uart2_mux",
+ apbc_base + APBC_UART2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
+
+ clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("ssp0", "ssp0_mux", apbc_base + APBC_SSP0,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.0");
+
+ clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("ssp1", "ssp1_mux", apbc_base + APBC_SSP1,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.1");
+
+ clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("ssp2", "ssp1_mux", apbc_base + APBC_SSP2,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.2");
+
+ clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.3", NULL);
+
+ clk = mmp_clk_register_apbc("ssp3", "ssp1_mux", apbc_base + APBC_SSP3,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.3");
+
+ clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.4", NULL);
+
+ clk = mmp_clk_register_apbc("ssp4", "ssp1_mux", apbc_base + APBC_SSP4,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.4");
+
+ clk = mmp_clk_register_apmu("dfc", "pll1_4", apmu_base + APMU_DFC,
+ 0x19b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");
+
+ clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh0_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh0", "sdh_mux", apmu_base + APMU_SDH0,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.0");
+
+ clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh1_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh1", "sdh1_mux", apmu_base + APMU_SDH1,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.1");
+
+ clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB,
+ 0x9, &clk_lock);
+ clk_register_clkdev(clk, "usb_clk", NULL);
+
+ clk = mmp_clk_register_apmu("sph", "usb_pll", apmu_base + APMU_USB,
+ 0x12, &clk_lock);
+ clk_register_clkdev(clk, "sph_clk", NULL);
+
+ clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0", "disp0_mux",
+ apmu_base + APMU_DISP0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-disp.0");
+
+ clk = mmp_clk_register_apmu("disp0_hclk", "disp0_mux",
+ apmu_base + APMU_DISP0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "hclk", "mmp-disp.0");
+
+ clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0", "ccic0_mux",
+ apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
+
+ clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
+ ARRAY_SIZE(ccic_phy_parent),
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 7, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
+ apmu_base + APMU_CCIC0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
+
+ clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
+ apmu_base + APMU_CCIC0, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
+}
diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c
new file mode 100644
index 000000000..6ec05698e
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa910.c
@@ -0,0 +1,320 @@
+/*
+ * pxa910 clock framework source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/addr-map.h>
+
+#include "clk.h"
+
+#define APBC_RTC 0x28
+#define APBC_TWSI0 0x2c
+#define APBC_KPC 0x18
+#define APBC_UART0 0x0
+#define APBC_UART1 0x4
+#define APBC_GPIO 0x8
+#define APBC_PWM0 0xc
+#define APBC_PWM1 0x10
+#define APBC_PWM2 0x14
+#define APBC_PWM3 0x18
+#define APBC_SSP0 0x1c
+#define APBC_SSP1 0x20
+#define APBC_SSP2 0x4c
+#define APBCP_TWSI1 0x28
+#define APBCP_UART2 0x1c
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_USB 0x5c
+#define APMU_DISP0 0x4c
+#define APMU_CCIC0 0x50
+#define APMU_DFC 0x60
+#define MPMU_UART_PLL 0x14
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = 0x1fff,
+ .den_mask = 0x1fff,
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static struct clk_factor_tbl uart_factor_tbl[] = {
+ {.num = 8125, .den = 1536}, /*14.745MHZ */
+};
+
+static const char *uart_parent[] = {"pll1_3_16", "uart_pll"};
+static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
+static const char *sdh_parent[] = {"pll1_12", "pll1_13"};
+static const char *disp_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"};
+
+void __init pxa910_clk_init(void)
+{
+ struct clk *clk;
+ struct clk *uart_pll;
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbcp_base;
+ void __iomem *apbc_base;
+
+ mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
+ if (mpmu_base == NULL) {
+ pr_err("error to ioremap MPMU base\n");
+ return;
+ }
+
+ apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
+ if (apmu_base == NULL) {
+ pr_err("error to ioremap APMU base\n");
+ return;
+ }
+
+ apbcp_base = ioremap(APB_PHYS_BASE + 0x3b000, SZ_4K);
+ if (apbcp_base == NULL) {
+ pr_err("error to ioremap APBC extension base\n");
+ return;
+ }
+
+ apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K);
+ if (apbc_base == NULL) {
+ pr_err("error to ioremap APBC base\n");
+ return;
+ }
+
+ clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200);
+ clk_register_clkdev(clk, "clk32", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk_register_clkdev(clk, "vctcxo", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT,
+ 624000000);
+ clk_register_clkdev(clk, "pll1", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll1_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_24", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_48", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_96", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1",
+ CLK_SET_RATE_PARENT, 1, 13);
+ clk_register_clkdev(clk, "pll1_13", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_13_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_2_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1",
+ CLK_SET_RATE_PARENT, 3, 16);
+ clk_register_clkdev(clk, "pll1_3_16", NULL);
+
+ uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
+ mpmu_base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl));
+ clk_set_rate(uart_pll, 14745600);
+ clk_register_clkdev(uart_pll, "uart_pll", NULL);
+
+ clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5",
+ apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
+
+ clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5",
+ apbcp_base + APBCP_TWSI1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
+
+ clk = mmp_clk_register_apbc("gpio", "vctcxo",
+ apbc_base + APBC_GPIO, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-gpio");
+
+ clk = mmp_clk_register_apbc("kpc", "clk32",
+ apbc_base + APBC_KPC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa27x-keypad");
+
+ clk = mmp_clk_register_apbc("rtc", "clk32",
+ apbc_base + APBC_RTC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sa1100-rtc");
+
+ clk = mmp_clk_register_apbc("pwm0", "pll1_48",
+ apbc_base + APBC_PWM0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.0");
+
+ clk = mmp_clk_register_apbc("pwm1", "pll1_48",
+ apbc_base + APBC_PWM1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.1");
+
+ clk = mmp_clk_register_apbc("pwm2", "pll1_48",
+ apbc_base + APBC_PWM2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.2");
+
+ clk = mmp_clk_register_apbc("pwm3", "pll1_48",
+ apbc_base + APBC_PWM3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.3");
+
+ clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("uart0", "uart0_mux",
+ apbc_base + APBC_UART0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
+
+ clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("uart1", "uart1_mux",
+ apbc_base + APBC_UART1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
+
+ clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("uart2", "uart2_mux",
+ apbcp_base + APBCP_UART2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
+
+ clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("ssp0", "ssp0_mux",
+ apbc_base + APBC_SSP0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.0");
+
+ clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("ssp1", "ssp1_mux",
+ apbc_base + APBC_SSP1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.1");
+
+ clk = mmp_clk_register_apmu("dfc", "pll1_4",
+ apmu_base + APMU_DFC, 0x19b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");
+
+ clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh0_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh0", "sdh_mux",
+ apmu_base + APMU_SDH0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.0");
+
+ clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh1_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh1", "sdh1_mux",
+ apmu_base + APMU_SDH1, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.1");
+
+ clk = mmp_clk_register_apmu("usb", "usb_pll",
+ apmu_base + APMU_USB, 0x9, &clk_lock);
+ clk_register_clkdev(clk, "usb_clk", NULL);
+
+ clk = mmp_clk_register_apmu("sph", "usb_pll",
+ apmu_base + APMU_USB, 0x12, &clk_lock);
+ clk_register_clkdev(clk, "sph_clk", NULL);
+
+ clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0", "disp0_mux",
+ apmu_base + APMU_DISP0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-disp.0");
+
+ clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0", "ccic0_mux",
+ apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
+
+ clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
+ ARRAY_SIZE(ccic_phy_parent),
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 7, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
+ apmu_base + APMU_CCIC0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
+
+ clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
+ apmu_base + APMU_CCIC0, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
+}
diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h
new file mode 100644
index 000000000..ab86dd4a4
--- /dev/null
+++ b/drivers/clk/mmp/clk.h
@@ -0,0 +1,35 @@
+#ifndef __MACH_MMP_CLK_H
+#define __MACH_MMP_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#define APBC_NO_BUS_CTRL BIT(0)
+#define APBC_POWER_CTRL BIT(1)
+
+struct clk_factor_masks {
+ unsigned int factor;
+ unsigned int num_mask;
+ unsigned int den_mask;
+ unsigned int num_shift;
+ unsigned int den_shift;
+};
+
+struct clk_factor_tbl {
+ unsigned int num;
+ unsigned int den;
+};
+
+extern struct clk *mmp_clk_register_pll2(const char *name,
+ const char *parent_name, unsigned long flags);
+extern struct clk *mmp_clk_register_apbc(const char *name,
+ const char *parent_name, void __iomem *base,
+ unsigned int delay, unsigned int apbc_flags, spinlock_t *lock);
+extern struct clk *mmp_clk_register_apmu(const char *name,
+ const char *parent_name, void __iomem *base, u32 enable_mask,
+ spinlock_t *lock);
+extern struct clk *mmp_clk_register_factor(const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *base, struct clk_factor_masks *masks,
+ struct clk_factor_tbl *ftbl, unsigned int ftbl_cnt);
+#endif
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
new file mode 100644
index 000000000..57323fd15
--- /dev/null
+++ b/drivers/clk/mvebu/Kconfig
@@ -0,0 +1,8 @@
+config MVEBU_CLK_CORE
+ bool
+
+config MVEBU_CLK_CPU
+ bool
+
+config MVEBU_CLK_GATING
+ bool
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
new file mode 100644
index 000000000..58df3dc49
--- /dev/null
+++ b/drivers/clk/mvebu/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o
+obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
+obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o
diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c
new file mode 100644
index 000000000..e5c477b49
--- /dev/null
+++ b/drivers/clk/mvebu/clk-core.c
@@ -0,0 +1,675 @@
+/*
+ * Marvell EBU clock core handling defined at reset
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include "clk-core.h"
+
+struct core_ratio {
+ int id;
+ const char *name;
+};
+
+struct core_clocks {
+ u32 (*get_tclk_freq)(void __iomem *sar);
+ u32 (*get_cpu_freq)(void __iomem *sar);
+ void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
+ const struct core_ratio *ratios;
+ int num_ratios;
+};
+
+static struct clk_onecell_data clk_data;
+
+static void __init mvebu_clk_core_setup(struct device_node *np,
+ struct core_clocks *coreclk)
+{
+ const char *tclk_name = "tclk";
+ const char *cpuclk_name = "cpuclk";
+ void __iomem *base;
+ unsigned long rate;
+ int n;
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return;
+
+ /*
+ * Allocate struct for TCLK, cpu clk, and core ratio clocks
+ */
+ clk_data.clk_num = 2 + coreclk->num_ratios;
+ clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_data.clks))
+ return;
+
+ /*
+ * Register TCLK
+ */
+ of_property_read_string_index(np, "clock-output-names", 0,
+ &tclk_name);
+ rate = coreclk->get_tclk_freq(base);
+ clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL,
+ CLK_IS_ROOT, rate);
+ WARN_ON(IS_ERR(clk_data.clks[0]));
+
+ /*
+ * Register CPU clock
+ */
+ of_property_read_string_index(np, "clock-output-names", 1,
+ &cpuclk_name);
+ rate = coreclk->get_cpu_freq(base);
+ clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
+ CLK_IS_ROOT, rate);
+ WARN_ON(IS_ERR(clk_data.clks[1]));
+
+ /*
+ * Register fixed-factor clocks derived from CPU clock
+ */
+ for (n = 0; n < coreclk->num_ratios; n++) {
+ const char *rclk_name = coreclk->ratios[n].name;
+ int mult, div;
+
+ of_property_read_string_index(np, "clock-output-names",
+ 2+n, &rclk_name);
+ coreclk->get_clk_ratio(base, coreclk->ratios[n].id,
+ &mult, &div);
+ clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name,
+ cpuclk_name, 0, mult, div);
+ WARN_ON(IS_ERR(clk_data.clks[2+n]));
+ };
+
+ /*
+ * SAR register isn't needed anymore
+ */
+ iounmap(base);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+#ifdef CONFIG_MACH_ARMADA_370_XP
+/*
+ * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two
+ * register of 32 bits
+ */
+
+#define SARL 0 /* Low part [0:31] */
+#define SARL_AXP_PCLK_FREQ_OPT 21
+#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7
+#define SARL_A370_PCLK_FREQ_OPT 11
+#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF
+#define SARL_AXP_FAB_FREQ_OPT 24
+#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF
+#define SARL_A370_FAB_FREQ_OPT 15
+#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F
+#define SARL_A370_TCLK_FREQ_OPT 20
+#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1
+#define SARH 4 /* High part [32:63] */
+#define SARH_AXP_PCLK_FREQ_OPT (52-32)
+#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1
+#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3
+#define SARH_AXP_FAB_FREQ_OPT (51-32)
+#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1
+#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4
+
+static const u32 __initconst armada_370_tclk_frequencies[] = {
+ 166000000,
+ 200000000,
+};
+
+static u32 __init armada_370_get_tclk_freq(void __iomem *sar)
+{
+ u8 tclk_freq_select = 0;
+
+ tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) &
+ SARL_A370_TCLK_FREQ_OPT_MASK);
+ return armada_370_tclk_frequencies[tclk_freq_select];
+}
+
+static const u32 __initconst armada_370_cpu_frequencies[] = {
+ 400000000,
+ 533000000,
+ 667000000,
+ 800000000,
+ 1000000000,
+ 1067000000,
+ 1200000000,
+};
+
+static u32 __init armada_370_get_cpu_freq(void __iomem *sar)
+{
+ u32 cpu_freq;
+ u8 cpu_freq_select = 0;
+
+ cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
+ SARL_A370_PCLK_FREQ_OPT_MASK);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) {
+ pr_err("CPU freq select unsupported %d\n", cpu_freq_select);
+ cpu_freq = 0;
+ } else
+ cpu_freq = armada_370_cpu_frequencies[cpu_freq_select];
+
+ return cpu_freq;
+}
+
+enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK };
+
+static const struct core_ratio __initconst armada_370_xp_core_ratios[] = {
+ { .id = A370_XP_NBCLK, .name = "nbclk" },
+ { .id = A370_XP_HCLK, .name = "hclk" },
+ { .id = A370_XP_DRAMCLK, .name = "dramclk" },
+};
+
+static const int __initconst armada_370_xp_nbclk_ratios[32][2] = {
+ {0, 1}, {1, 2}, {2, 2}, {2, 2},
+ {1, 2}, {1, 2}, {1, 1}, {2, 3},
+ {0, 1}, {1, 2}, {2, 4}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {2, 2},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {2, 3}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int __initconst armada_370_xp_hclk_ratios[32][2] = {
+ {0, 1}, {1, 2}, {2, 6}, {2, 3},
+ {1, 3}, {1, 4}, {1, 2}, {2, 6},
+ {0, 1}, {1, 6}, {2, 10}, {0, 1},
+ {1, 4}, {0, 1}, {0, 1}, {2, 5},
+ {0, 1}, {0, 1}, {0, 1}, {1, 2},
+ {2, 6}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int __initconst armada_370_xp_dramclk_ratios[32][2] = {
+ {0, 1}, {1, 2}, {2, 3}, {2, 3},
+ {1, 3}, {1, 2}, {1, 2}, {2, 6},
+ {0, 1}, {1, 3}, {2, 5}, {0, 1},
+ {1, 4}, {0, 1}, {0, 1}, {2, 5},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {2, 3}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static void __init armada_370_xp_get_clk_ratio(u32 opt,
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case A370_XP_NBCLK:
+ *mult = armada_370_xp_nbclk_ratios[opt][0];
+ *div = armada_370_xp_nbclk_ratios[opt][1];
+ break;
+ case A370_XP_HCLK:
+ *mult = armada_370_xp_hclk_ratios[opt][0];
+ *div = armada_370_xp_hclk_ratios[opt][1];
+ break;
+ case A370_XP_DRAMCLK:
+ *mult = armada_370_xp_dramclk_ratios[opt][0];
+ *div = armada_370_xp_dramclk_ratios[opt][1];
+ break;
+ }
+}
+
+static void __init armada_370_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) &
+ SARL_A370_FAB_FREQ_OPT_MASK);
+
+ armada_370_xp_get_clk_ratio(opt, sar, id, mult, div);
+}
+
+
+static const struct core_clocks armada_370_core_clocks = {
+ .get_tclk_freq = armada_370_get_tclk_freq,
+ .get_cpu_freq = armada_370_get_cpu_freq,
+ .get_clk_ratio = armada_370_get_clk_ratio,
+ .ratios = armada_370_xp_core_ratios,
+ .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios),
+};
+
+static const u32 __initconst armada_xp_cpu_frequencies[] = {
+ 1000000000,
+ 1066000000,
+ 1200000000,
+ 1333000000,
+ 1500000000,
+ 1666000000,
+ 1800000000,
+ 2000000000,
+ 667000000,
+ 0,
+ 800000000,
+ 1600000000,
+};
+
+/* For Armada XP TCLK frequency is fix: 250MHz */
+static u32 __init armada_xp_get_tclk_freq(void __iomem *sar)
+{
+ return 250 * 1000 * 1000;
+}
+
+static u32 __init armada_xp_get_cpu_freq(void __iomem *sar)
+{
+ u32 cpu_freq;
+ u8 cpu_freq_select = 0;
+
+ cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) &
+ SARL_AXP_PCLK_FREQ_OPT_MASK);
+ /*
+ * The upper bit is not contiguous to the other ones and
+ * located in the high part of the SAR registers
+ */
+ cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) &
+ SARH_AXP_PCLK_FREQ_OPT_MASK)
+ << SARH_AXP_PCLK_FREQ_OPT_SHIFT);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) {
+ pr_err("CPU freq select unsupported: %d\n", cpu_freq_select);
+ cpu_freq = 0;
+ } else
+ cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select];
+
+ return cpu_freq;
+}
+
+static void __init armada_xp_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+
+ u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) &
+ SARL_AXP_FAB_FREQ_OPT_MASK);
+ /*
+ * The upper bit is not contiguous to the other ones and
+ * located in the high part of the SAR registers
+ */
+ opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) &
+ SARH_AXP_FAB_FREQ_OPT_MASK)
+ << SARH_AXP_FAB_FREQ_OPT_SHIFT);
+
+ armada_370_xp_get_clk_ratio(opt, sar, id, mult, div);
+}
+
+static const struct core_clocks armada_xp_core_clocks = {
+ .get_tclk_freq = armada_xp_get_tclk_freq,
+ .get_cpu_freq = armada_xp_get_cpu_freq,
+ .get_clk_ratio = armada_xp_get_clk_ratio,
+ .ratios = armada_370_xp_core_ratios,
+ .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios),
+};
+
+#endif /* CONFIG_MACH_ARMADA_370_XP */
+
+/*
+ * Dove PLL sample-at-reset configuration
+ *
+ * SAR0[8:5] : CPU frequency
+ * 5 = 1000 MHz
+ * 6 = 933 MHz
+ * 7 = 933 MHz
+ * 8 = 800 MHz
+ * 9 = 800 MHz
+ * 10 = 800 MHz
+ * 11 = 1067 MHz
+ * 12 = 667 MHz
+ * 13 = 533 MHz
+ * 14 = 400 MHz
+ * 15 = 333 MHz
+ * others reserved.
+ *
+ * SAR0[11:9] : CPU to L2 Clock divider ratio
+ * 0 = (1/1) * CPU
+ * 2 = (1/2) * CPU
+ * 4 = (1/3) * CPU
+ * 6 = (1/4) * CPU
+ * others reserved.
+ *
+ * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio
+ * 0 = (1/1) * CPU
+ * 2 = (1/2) * CPU
+ * 3 = (2/5) * CPU
+ * 4 = (1/3) * CPU
+ * 6 = (1/4) * CPU
+ * 8 = (1/5) * CPU
+ * 10 = (1/6) * CPU
+ * 12 = (1/7) * CPU
+ * 14 = (1/8) * CPU
+ * 15 = (1/10) * CPU
+ * others reserved.
+ *
+ * SAR0[24:23] : TCLK frequency
+ * 0 = 166 MHz
+ * 1 = 125 MHz
+ * others reserved.
+ */
+#ifdef CONFIG_ARCH_DOVE
+#define SAR_DOVE_CPU_FREQ 5
+#define SAR_DOVE_CPU_FREQ_MASK 0xf
+#define SAR_DOVE_L2_RATIO 9
+#define SAR_DOVE_L2_RATIO_MASK 0x7
+#define SAR_DOVE_DDR_RATIO 12
+#define SAR_DOVE_DDR_RATIO_MASK 0xf
+#define SAR_DOVE_TCLK_FREQ 23
+#define SAR_DOVE_TCLK_FREQ_MASK 0x3
+
+static const u32 __initconst dove_tclk_frequencies[] = {
+ 166666667,
+ 125000000,
+ 0, 0
+};
+
+static u32 __init dove_get_tclk_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) &
+ SAR_DOVE_TCLK_FREQ_MASK;
+ return dove_tclk_frequencies[opt];
+}
+
+static const u32 __initconst dove_cpu_frequencies[] = {
+ 0, 0, 0, 0, 0,
+ 1000000000,
+ 933333333, 933333333,
+ 800000000, 800000000, 800000000,
+ 1066666667,
+ 666666667,
+ 533333333,
+ 400000000,
+ 333333333
+};
+
+static u32 __init dove_get_cpu_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) &
+ SAR_DOVE_CPU_FREQ_MASK;
+ return dove_cpu_frequencies[opt];
+}
+
+enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR };
+
+static const struct core_ratio __initconst dove_core_ratios[] = {
+ { .id = DOVE_CPU_TO_L2, .name = "l2clk", },
+ { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", }
+};
+
+static const int __initconst dove_cpu_l2_ratios[8][2] = {
+ { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 },
+ { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }
+};
+
+static const int __initconst dove_cpu_ddr_ratios[16][2] = {
+ { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 },
+ { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 },
+ { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 },
+ { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 }
+};
+
+static void __init dove_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case DOVE_CPU_TO_L2:
+ {
+ u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) &
+ SAR_DOVE_L2_RATIO_MASK;
+ *mult = dove_cpu_l2_ratios[opt][0];
+ *div = dove_cpu_l2_ratios[opt][1];
+ break;
+ }
+ case DOVE_CPU_TO_DDR:
+ {
+ u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) &
+ SAR_DOVE_DDR_RATIO_MASK;
+ *mult = dove_cpu_ddr_ratios[opt][0];
+ *div = dove_cpu_ddr_ratios[opt][1];
+ break;
+ }
+ }
+}
+
+static const struct core_clocks dove_core_clocks = {
+ .get_tclk_freq = dove_get_tclk_freq,
+ .get_cpu_freq = dove_get_cpu_freq,
+ .get_clk_ratio = dove_get_clk_ratio,
+ .ratios = dove_core_ratios,
+ .num_ratios = ARRAY_SIZE(dove_core_ratios),
+};
+#endif /* CONFIG_ARCH_DOVE */
+
+/*
+ * Kirkwood PLL sample-at-reset configuration
+ * (6180 has different SAR layout than other Kirkwood SoCs)
+ *
+ * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282)
+ * 4 = 600 MHz
+ * 6 = 800 MHz
+ * 7 = 1000 MHz
+ * 9 = 1200 MHz
+ * 12 = 1500 MHz
+ * 13 = 1600 MHz
+ * 14 = 1800 MHz
+ * 15 = 2000 MHz
+ * others reserved.
+ *
+ * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282)
+ * 1 = (1/2) * CPU
+ * 3 = (1/3) * CPU
+ * 5 = (1/4) * CPU
+ * others reserved.
+ *
+ * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282)
+ * 2 = (1/2) * CPU
+ * 4 = (1/3) * CPU
+ * 6 = (1/4) * CPU
+ * 7 = (2/9) * CPU
+ * 8 = (1/5) * CPU
+ * 9 = (1/6) * CPU
+ * others reserved.
+ *
+ * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only)
+ * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU]
+ * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU]
+ * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU]
+ * others reserved.
+ *
+ * SAR0[21] : TCLK frequency
+ * 0 = 200 MHz
+ * 1 = 166 MHz
+ * others reserved.
+ */
+#ifdef CONFIG_ARCH_KIRKWOOD
+#define SAR_KIRKWOOD_CPU_FREQ(x) \
+ (((x & (1 << 1)) >> 1) | \
+ ((x & (1 << 22)) >> 21) | \
+ ((x & (3 << 3)) >> 1))
+#define SAR_KIRKWOOD_L2_RATIO(x) \
+ (((x & (3 << 9)) >> 9) | \
+ (((x & (1 << 19)) >> 17)))
+#define SAR_KIRKWOOD_DDR_RATIO 5
+#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf
+#define SAR_MV88F6180_CLK 2
+#define SAR_MV88F6180_CLK_MASK 0x7
+#define SAR_KIRKWOOD_TCLK_FREQ 21
+#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1
+
+enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR };
+
+static const struct core_ratio __initconst kirkwood_core_ratios[] = {
+ { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", },
+ { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", }
+};
+
+static u32 __init kirkwood_get_tclk_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) &
+ SAR_KIRKWOOD_TCLK_FREQ_MASK;
+ return (opt) ? 166666667 : 200000000;
+}
+
+static const u32 __initconst kirkwood_cpu_frequencies[] = {
+ 0, 0, 0, 0,
+ 600000000,
+ 0,
+ 800000000,
+ 1000000000,
+ 0,
+ 1200000000,
+ 0, 0,
+ 1500000000,
+ 1600000000,
+ 1800000000,
+ 2000000000
+};
+
+static u32 __init kirkwood_get_cpu_freq(void __iomem *sar)
+{
+ u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar));
+ return kirkwood_cpu_frequencies[opt];
+}
+
+static const int __initconst kirkwood_cpu_l2_ratios[8][2] = {
+ { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 },
+ { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 }
+};
+
+static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = {
+ { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 },
+ { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 },
+ { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 },
+ { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }
+};
+
+static void __init kirkwood_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case KIRKWOOD_CPU_TO_L2:
+ {
+ u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar));
+ *mult = kirkwood_cpu_l2_ratios[opt][0];
+ *div = kirkwood_cpu_l2_ratios[opt][1];
+ break;
+ }
+ case KIRKWOOD_CPU_TO_DDR:
+ {
+ u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) &
+ SAR_KIRKWOOD_DDR_RATIO_MASK;
+ *mult = kirkwood_cpu_ddr_ratios[opt][0];
+ *div = kirkwood_cpu_ddr_ratios[opt][1];
+ break;
+ }
+ }
+}
+
+static const struct core_clocks kirkwood_core_clocks = {
+ .get_tclk_freq = kirkwood_get_tclk_freq,
+ .get_cpu_freq = kirkwood_get_cpu_freq,
+ .get_clk_ratio = kirkwood_get_clk_ratio,
+ .ratios = kirkwood_core_ratios,
+ .num_ratios = ARRAY_SIZE(kirkwood_core_ratios),
+};
+
+static const u32 __initconst mv88f6180_cpu_frequencies[] = {
+ 0, 0, 0, 0, 0,
+ 600000000,
+ 800000000,
+ 1000000000
+};
+
+static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK;
+ return mv88f6180_cpu_frequencies[opt];
+}
+
+static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = {
+ { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 },
+ { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 }
+};
+
+static void __init mv88f6180_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case KIRKWOOD_CPU_TO_L2:
+ {
+ /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */
+ *mult = 1;
+ *div = 2;
+ break;
+ }
+ case KIRKWOOD_CPU_TO_DDR:
+ {
+ u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) &
+ SAR_MV88F6180_CLK_MASK;
+ *mult = mv88f6180_cpu_ddr_ratios[opt][0];
+ *div = mv88f6180_cpu_ddr_ratios[opt][1];
+ break;
+ }
+ }
+}
+
+static const struct core_clocks mv88f6180_core_clocks = {
+ .get_tclk_freq = kirkwood_get_tclk_freq,
+ .get_cpu_freq = mv88f6180_get_cpu_freq,
+ .get_clk_ratio = mv88f6180_get_clk_ratio,
+ .ratios = kirkwood_core_ratios,
+ .num_ratios = ARRAY_SIZE(kirkwood_core_ratios),
+};
+#endif /* CONFIG_ARCH_KIRKWOOD */
+
+static const __initdata struct of_device_id clk_core_match[] = {
+#ifdef CONFIG_MACH_ARMADA_370_XP
+ {
+ .compatible = "marvell,armada-370-core-clock",
+ .data = &armada_370_core_clocks,
+ },
+ {
+ .compatible = "marvell,armada-xp-core-clock",
+ .data = &armada_xp_core_clocks,
+ },
+#endif
+#ifdef CONFIG_ARCH_DOVE
+ {
+ .compatible = "marvell,dove-core-clock",
+ .data = &dove_core_clocks,
+ },
+#endif
+
+#ifdef CONFIG_ARCH_KIRKWOOD
+ {
+ .compatible = "marvell,kirkwood-core-clock",
+ .data = &kirkwood_core_clocks,
+ },
+ {
+ .compatible = "marvell,mv88f6180-core-clock",
+ .data = &mv88f6180_core_clocks,
+ },
+#endif
+
+ { }
+};
+
+void __init mvebu_core_clk_init(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, clk_core_match) {
+ const struct of_device_id *match =
+ of_match_node(clk_core_match, np);
+ mvebu_clk_core_setup(np, (struct core_clocks *)match->data);
+ }
+}
diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h
new file mode 100644
index 000000000..28b5e02e9
--- /dev/null
+++ b/drivers/clk/mvebu/clk-core.h
@@ -0,0 +1,18 @@
+/*
+ * * Marvell EBU clock core handling defined at reset
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MVEBU_CLK_CORE_H
+#define __MVEBU_CLK_CORE_H
+
+void __init mvebu_core_clk_init(void);
+
+#endif
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c
new file mode 100644
index 000000000..b0fbc0715
--- /dev/null
+++ b/drivers/clk/mvebu/clk-cpu.c
@@ -0,0 +1,176 @@
+/*
+ * Marvell MVEBU CPU clock handling.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+
+#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
+#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
+#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F
+
+#define MAX_CPU 4
+struct cpu_clk {
+ struct clk_hw hw;
+ int cpu;
+ const char *clk_name;
+ const char *parent_name;
+ void __iomem *reg_base;
+};
+
+static struct clk **clks;
+
+static struct clk_onecell_data clk_data;
+
+#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw)
+
+static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
+ u32 reg, div;
+
+ reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
+ div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK;
+ return parent_rate / div;
+}
+
+static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /* Valid ratio are 1:1, 1:2 and 1:3 */
+ u32 div;
+
+ div = *parent_rate / rate;
+ if (div == 0)
+ div = 1;
+ else if (div > 3)
+ div = 3;
+
+ return *parent_rate / div;
+}
+
+static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
+ u32 reg, div;
+ u32 reload_mask;
+
+ div = parent_rate / rate;
+ reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET)
+ & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8))))
+ | (div << (cpuclk->cpu * 8));
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
+ /* Set clock divider reload smooth bit mask */
+ reload_mask = 1 << (20 + cpuclk->cpu);
+
+ reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
+ | reload_mask;
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
+
+ /* Now trigger the clock update */
+ reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
+ | 1 << 24;
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
+
+ /* Wait for clocks to settle down then clear reload request */
+ udelay(1000);
+ reg &= ~(reload_mask | 1 << 24);
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
+ udelay(1000);
+
+ return 0;
+}
+
+static const struct clk_ops cpu_ops = {
+ .recalc_rate = clk_cpu_recalc_rate,
+ .round_rate = clk_cpu_round_rate,
+ .set_rate = clk_cpu_set_rate,
+};
+
+void __init of_cpu_clk_setup(struct device_node *node)
+{
+ struct cpu_clk *cpuclk;
+ void __iomem *clock_complex_base = of_iomap(node, 0);
+ int ncpus = 0;
+ struct device_node *dn;
+
+ if (clock_complex_base == NULL) {
+ pr_err("%s: clock-complex base register not set\n",
+ __func__);
+ return;
+ }
+
+ for_each_node_by_type(dn, "cpu")
+ ncpus++;
+
+ cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL);
+ if (WARN_ON(!cpuclk))
+ return;
+
+ clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL);
+ if (WARN_ON(!clks))
+ goto clks_out;
+
+ for_each_node_by_type(dn, "cpu") {
+ struct clk_init_data init;
+ struct clk *clk;
+ struct clk *parent_clk;
+ char *clk_name = kzalloc(5, GFP_KERNEL);
+ int cpu, err;
+
+ if (WARN_ON(!clk_name))
+ goto bail_out;
+
+ err = of_property_read_u32(dn, "reg", &cpu);
+ if (WARN_ON(err))
+ goto bail_out;
+
+ sprintf(clk_name, "cpu%d", cpu);
+ parent_clk = of_clk_get(node, 0);
+
+ cpuclk[cpu].parent_name = __clk_get_name(parent_clk);
+ cpuclk[cpu].clk_name = clk_name;
+ cpuclk[cpu].cpu = cpu;
+ cpuclk[cpu].reg_base = clock_complex_base;
+ cpuclk[cpu].hw.init = &init;
+
+ init.name = cpuclk[cpu].clk_name;
+ init.ops = &cpu_ops;
+ init.flags = 0;
+ init.parent_names = &cpuclk[cpu].parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &cpuclk[cpu].hw);
+ if (WARN_ON(IS_ERR(clk)))
+ goto bail_out;
+ clks[cpu] = clk;
+ }
+ clk_data.clk_num = MAX_CPU;
+ clk_data.clks = clks;
+ of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
+
+ return;
+bail_out:
+ kfree(clks);
+ while(ncpus--)
+ kfree(cpuclk[ncpus].clk_name);
+clks_out:
+ kfree(cpuclk);
+}
+
+CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
+ of_cpu_clk_setup);
diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c
new file mode 100644
index 000000000..ebf141d43
--- /dev/null
+++ b/drivers/clk/mvebu/clk-gating-ctrl.c
@@ -0,0 +1,250 @@
+/*
+ * Marvell MVEBU clock gating control.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/mvebu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+struct mvebu_gating_ctrl {
+ spinlock_t lock;
+ struct clk **gates;
+ int num_gates;
+};
+
+struct mvebu_soc_descr {
+ const char *name;
+ const char *parent;
+ int bit_idx;
+};
+
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+
+static struct clk *mvebu_clk_gating_get_src(
+ struct of_phandle_args *clkspec, void *data)
+{
+ struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data;
+ int n;
+
+ if (clkspec->args_count < 1)
+ return ERR_PTR(-EINVAL);
+
+ for (n = 0; n < ctrl->num_gates; n++) {
+ struct clk_gate *gate =
+ to_clk_gate(__clk_get_hw(ctrl->gates[n]));
+ if (clkspec->args[0] == gate->bit_idx)
+ return ctrl->gates[n];
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+static void __init mvebu_clk_gating_setup(
+ struct device_node *np, const struct mvebu_soc_descr *descr)
+{
+ struct mvebu_gating_ctrl *ctrl;
+ struct clk *clk;
+ void __iomem *base;
+ const char *default_parent = NULL;
+ int n;
+
+ base = of_iomap(np, 0);
+
+ clk = of_clk_get(np, 0);
+ if (!IS_ERR(clk)) {
+ default_parent = __clk_get_name(clk);
+ clk_put(clk);
+ }
+
+ ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL);
+ if (WARN_ON(!ctrl))
+ return;
+
+ spin_lock_init(&ctrl->lock);
+
+ /*
+ * Count, allocate, and register clock gates
+ */
+ for (n = 0; descr[n].name;)
+ n++;
+
+ ctrl->num_gates = n;
+ ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!ctrl->gates)) {
+ kfree(ctrl);
+ return;
+ }
+
+ for (n = 0; n < ctrl->num_gates; n++) {
+ u8 flags = 0;
+ const char *parent =
+ (descr[n].parent) ? descr[n].parent : default_parent;
+
+ /*
+ * On Armada 370, the DDR clock is a special case: it
+ * isn't taken by any driver, but should anyway be
+ * kept enabled, so we mark it as IGNORE_UNUSED for
+ * now.
+ */
+ if (!strcmp(descr[n].name, "ddr"))
+ flags |= CLK_IGNORE_UNUSED;
+
+ ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent,
+ flags, base, descr[n].bit_idx, 0, &ctrl->lock);
+ WARN_ON(IS_ERR(ctrl->gates[n]));
+ }
+ of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl);
+}
+
+/*
+ * SoC specific clock gating control
+ */
+
+#ifdef CONFIG_MACH_ARMADA_370
+static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = {
+ { "audio", NULL, 0 },
+ { "pex0_en", NULL, 1 },
+ { "pex1_en", NULL, 2 },
+ { "ge1", NULL, 3 },
+ { "ge0", NULL, 4 },
+ { "pex0", NULL, 5 },
+ { "pex1", NULL, 9 },
+ { "sata0", NULL, 15 },
+ { "sdio", NULL, 17 },
+ { "tdm", NULL, 25 },
+ { "ddr", NULL, 28 },
+ { "sata1", NULL, 30 },
+ { }
+};
+#endif
+
+#ifdef CONFIG_MACH_ARMADA_XP
+static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = {
+ { "audio", NULL, 0 },
+ { "ge3", NULL, 1 },
+ { "ge2", NULL, 2 },
+ { "ge1", NULL, 3 },
+ { "ge0", NULL, 4 },
+ { "pex0", NULL, 5 },
+ { "pex1", NULL, 6 },
+ { "pex2", NULL, 7 },
+ { "pex3", NULL, 8 },
+ { "bp", NULL, 13 },
+ { "sata0lnk", NULL, 14 },
+ { "sata0", "sata0lnk", 15 },
+ { "lcd", NULL, 16 },
+ { "sdio", NULL, 17 },
+ { "usb0", NULL, 18 },
+ { "usb1", NULL, 19 },
+ { "usb2", NULL, 20 },
+ { "xor0", NULL, 22 },
+ { "crypto", NULL, 23 },
+ { "tdm", NULL, 25 },
+ { "xor1", NULL, 28 },
+ { "sata1lnk", NULL, 29 },
+ { "sata1", "sata1lnk", 30 },
+ { }
+};
+#endif
+
+#ifdef CONFIG_ARCH_DOVE
+static const struct mvebu_soc_descr __initconst dove_gating_descr[] = {
+ { "usb0", NULL, 0 },
+ { "usb1", NULL, 1 },
+ { "ge", "gephy", 2 },
+ { "sata", NULL, 3 },
+ { "pex0", NULL, 4 },
+ { "pex1", NULL, 5 },
+ { "sdio0", NULL, 8 },
+ { "sdio1", NULL, 9 },
+ { "nand", NULL, 10 },
+ { "camera", NULL, 11 },
+ { "i2s0", NULL, 12 },
+ { "i2s1", NULL, 13 },
+ { "crypto", NULL, 15 },
+ { "ac97", NULL, 21 },
+ { "pdma", NULL, 22 },
+ { "xor0", NULL, 23 },
+ { "xor1", NULL, 24 },
+ { "gephy", NULL, 30 },
+ { }
+};
+#endif
+
+#ifdef CONFIG_ARCH_KIRKWOOD
+static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = {
+ { "ge0", NULL, 0 },
+ { "pex0", NULL, 2 },
+ { "usb0", NULL, 3 },
+ { "sdio", NULL, 4 },
+ { "tsu", NULL, 5 },
+ { "runit", NULL, 7 },
+ { "xor0", NULL, 8 },
+ { "audio", NULL, 9 },
+ { "powersave", "cpuclk", 11 },
+ { "sata0", NULL, 14 },
+ { "sata1", NULL, 15 },
+ { "xor1", NULL, 16 },
+ { "crypto", NULL, 17 },
+ { "pex1", NULL, 18 },
+ { "ge1", NULL, 19 },
+ { "tdm", NULL, 20 },
+ { }
+};
+#endif
+
+static const __initdata struct of_device_id clk_gating_match[] = {
+#ifdef CONFIG_MACH_ARMADA_370
+ {
+ .compatible = "marvell,armada-370-gating-clock",
+ .data = armada_370_gating_descr,
+ },
+#endif
+
+#ifdef CONFIG_MACH_ARMADA_XP
+ {
+ .compatible = "marvell,armada-xp-gating-clock",
+ .data = armada_xp_gating_descr,
+ },
+#endif
+
+#ifdef CONFIG_ARCH_DOVE
+ {
+ .compatible = "marvell,dove-gating-clock",
+ .data = dove_gating_descr,
+ },
+#endif
+
+#ifdef CONFIG_ARCH_KIRKWOOD
+ {
+ .compatible = "marvell,kirkwood-gating-clock",
+ .data = kirkwood_gating_descr,
+ },
+#endif
+
+ { }
+};
+
+void __init mvebu_gating_clk_init(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, clk_gating_match) {
+ const struct of_device_id *match =
+ of_match_node(clk_gating_match, np);
+ mvebu_clk_gating_setup(np,
+ (const struct mvebu_soc_descr *)match->data);
+ }
+}
diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h
new file mode 100644
index 000000000..9275d1e51
--- /dev/null
+++ b/drivers/clk/mvebu/clk-gating-ctrl.h
@@ -0,0 +1,22 @@
+/*
+ * Marvell EBU gating clock handling
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MVEBU_CLK_GATING_H
+#define __MVEBU_CLK_GATING_H
+
+#ifdef CONFIG_MVEBU_CLK_GATING
+void __init mvebu_gating_clk_init(void);
+#else
+void mvebu_gating_clk_init(void) {}
+#endif
+
+#endif
diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c
new file mode 100644
index 000000000..29f10fb30
--- /dev/null
+++ b/drivers/clk/mvebu/clk.c
@@ -0,0 +1,23 @@
+/*
+ * Marvell EBU SoC clock handling.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include "clk-core.h"
+#include "clk-gating-ctrl.h"
+
+void __init mvebu_clocks_init(void)
+{
+ mvebu_core_clk_init();
+ mvebu_gating_clk_init();
+ of_clk_init(NULL);
+}
diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile
new file mode 100644
index 000000000..a6a22237e
--- /dev/null
+++ b/drivers/clk/mxs/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for mxs specific clk
+#
+
+obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o clk-ssp.o
+
+obj-$(CONFIG_SOC_IMX23) += clk-imx23.o
+obj-$(CONFIG_SOC_IMX28) += clk-imx28.o
diff --git a/drivers/clk/mxs/clk-div.c b/drivers/clk/mxs/clk-div.c
new file mode 100644
index 000000000..90e1da938
--- /dev/null
+++ b/drivers/clk/mxs/clk-div.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+/**
+ * struct clk_div - mxs integer divider clock
+ * @divider: the parent class
+ * @ops: pointer to clk_ops of parent class
+ * @reg: register address
+ * @busy: busy bit shift
+ *
+ * The mxs divider clock is a subclass of basic clk_divider with an
+ * addtional busy bit.
+ */
+struct clk_div {
+ struct clk_divider divider;
+ const struct clk_ops *ops;
+ void __iomem *reg;
+ u8 busy;
+};
+
+static inline struct clk_div *to_clk_div(struct clk_hw *hw)
+{
+ struct clk_divider *divider = container_of(hw, struct clk_divider, hw);
+
+ return container_of(divider, struct clk_div, divider);
+}
+
+static unsigned long clk_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_div *div = to_clk_div(hw);
+
+ return div->ops->recalc_rate(&div->divider.hw, parent_rate);
+}
+
+static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_div *div = to_clk_div(hw);
+
+ return div->ops->round_rate(&div->divider.hw, rate, prate);
+}
+
+static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_div *div = to_clk_div(hw);
+ int ret;
+
+ ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate);
+ if (!ret)
+ ret = mxs_clk_wait(div->reg, div->busy);
+
+ return ret;
+}
+
+static struct clk_ops clk_div_ops = {
+ .recalc_rate = clk_div_recalc_rate,
+ .round_rate = clk_div_round_rate,
+ .set_rate = clk_div_set_rate,
+};
+
+struct clk *mxs_clk_div(const char *name, const char *parent_name,
+ void __iomem *reg, u8 shift, u8 width, u8 busy)
+{
+ struct clk_div *div;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_div_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ div->reg = reg;
+ div->busy = busy;
+
+ div->divider.reg = reg;
+ div->divider.shift = shift;
+ div->divider.width = width;
+ div->divider.flags = CLK_DIVIDER_ONE_BASED;
+ div->divider.lock = &mxs_lock;
+ div->divider.hw.init = &init;
+ div->ops = &clk_divider_ops;
+
+ clk = clk_register(NULL, &div->divider.hw);
+ if (IS_ERR(clk))
+ kfree(div);
+
+ return clk;
+}
diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c
new file mode 100644
index 000000000..e6aa6b567
--- /dev/null
+++ b/drivers/clk/mxs/clk-frac.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+/**
+ * struct clk_frac - mxs fractional divider clock
+ * @hw: clk_hw for the fractional divider clock
+ * @reg: register address
+ * @shift: the divider bit shift
+ * @width: the divider bit width
+ * @busy: busy bit shift
+ *
+ * The clock is an adjustable fractional divider with a busy bit to wait
+ * when the divider is adjusted.
+ */
+struct clk_frac {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 shift;
+ u8 width;
+ u8 busy;
+};
+
+#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
+
+static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ u32 div;
+
+ div = readl_relaxed(frac->reg) >> frac->shift;
+ div &= (1 << frac->width) - 1;
+
+ return (parent_rate >> frac->width) * div;
+}
+
+static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ unsigned long parent_rate = *prate;
+ u32 div;
+ u64 tmp;
+
+ if (rate > parent_rate)
+ return -EINVAL;
+
+ tmp = rate;
+ tmp <<= frac->width;
+ do_div(tmp, parent_rate);
+ div = tmp;
+
+ if (!div)
+ return -EINVAL;
+
+ return (parent_rate >> frac->width) * div;
+}
+
+static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ unsigned long flags;
+ u32 div, val;
+ u64 tmp;
+
+ if (rate > parent_rate)
+ return -EINVAL;
+
+ tmp = rate;
+ tmp <<= frac->width;
+ do_div(tmp, parent_rate);
+ div = tmp;
+
+ if (!div)
+ return -EINVAL;
+
+ spin_lock_irqsave(&mxs_lock, flags);
+
+ val = readl_relaxed(frac->reg);
+ val &= ~(((1 << frac->width) - 1) << frac->shift);
+ val |= div << frac->shift;
+ writel_relaxed(val, frac->reg);
+
+ spin_unlock_irqrestore(&mxs_lock, flags);
+
+ return mxs_clk_wait(frac->reg, frac->busy);
+}
+
+static struct clk_ops clk_frac_ops = {
+ .recalc_rate = clk_frac_recalc_rate,
+ .round_rate = clk_frac_round_rate,
+ .set_rate = clk_frac_set_rate,
+};
+
+struct clk *mxs_clk_frac(const char *name, const char *parent_name,
+ void __iomem *reg, u8 shift, u8 width, u8 busy)
+{
+ struct clk_frac *frac;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ frac = kzalloc(sizeof(*frac), GFP_KERNEL);
+ if (!frac)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_frac_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ frac->reg = reg;
+ frac->shift = shift;
+ frac->width = width;
+ frac->busy = busy;
+ frac->hw.init = &init;
+
+ clk = clk_register(NULL, &frac->hw);
+ if (IS_ERR(clk))
+ kfree(frac);
+
+ return clk;
+}
diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c
new file mode 100644
index 000000000..f6a74872f
--- /dev/null
+++ b/drivers/clk/mxs/clk-imx23.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include "clk.h"
+
+static void __iomem *clkctrl;
+static void __iomem *digctrl;
+
+#define CLKCTRL clkctrl
+#define DIGCTRL digctrl
+
+#define PLLCTRL0 (CLKCTRL + 0x0000)
+#define CPU (CLKCTRL + 0x0020)
+#define HBUS (CLKCTRL + 0x0030)
+#define XBUS (CLKCTRL + 0x0040)
+#define XTAL (CLKCTRL + 0x0050)
+#define PIX (CLKCTRL + 0x0060)
+#define SSP (CLKCTRL + 0x0070)
+#define GPMI (CLKCTRL + 0x0080)
+#define SPDIF (CLKCTRL + 0x0090)
+#define EMI (CLKCTRL + 0x00a0)
+#define SAIF (CLKCTRL + 0x00c0)
+#define TV (CLKCTRL + 0x00d0)
+#define ETM (CLKCTRL + 0x00e0)
+#define FRAC (CLKCTRL + 0x00f0)
+#define CLKSEQ (CLKCTRL + 0x0110)
+
+#define BP_CPU_INTERRUPT_WAIT 12
+#define BP_CLKSEQ_BYPASS_SAIF 0
+#define BP_CLKSEQ_BYPASS_SSP 5
+#define BP_SAIF_DIV_FRAC_EN 16
+#define BP_FRAC_IOFRAC 24
+
+static void __init clk_misc_init(void)
+{
+ u32 val;
+
+ /* Gate off cpu clock in WFI for power saving */
+ writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET);
+
+ /* Clear BYPASS for SAIF */
+ writel_relaxed(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ + CLR);
+
+ /* SAIF has to use frac div for functional operation */
+ val = readl_relaxed(SAIF);
+ val |= 1 << BP_SAIF_DIV_FRAC_EN;
+ writel_relaxed(val, SAIF);
+
+ /*
+ * Source ssp clock from ref_io than ref_xtal,
+ * as ref_xtal only provides 24 MHz as maximum.
+ */
+ writel_relaxed(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ + CLR);
+
+ /*
+ * 480 MHz seems too high to be ssp clock source directly,
+ * so set frac to get a 288 MHz ref_io.
+ */
+ writel_relaxed(0x3f << BP_FRAC_IOFRAC, FRAC + CLR);
+ writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET);
+}
+
+static const char *sel_pll[] __initconst = { "pll", "ref_xtal", };
+static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", };
+static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", };
+static const char *sel_io[] __initconst = { "ref_io", "ref_xtal", };
+static const char *cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", };
+static const char *emi_sels[] __initconst = { "emi_pll", "emi_xtal", };
+
+enum imx23_clk {
+ ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel,
+ lcdif_sel, gpmi_sel, ssp_sel, emi_sel, cpu, etm_sel, cpu_pll,
+ cpu_xtal, hbus, xbus, lcdif_div, ssp_div, gpmi_div, emi_pll,
+ emi_xtal, etm_div, saif_div, clk32k_div, rtc, adc, spdif_div,
+ clk32k, dri, pwm, filt, uart, ssp, gpmi, spdif, emi, saif,
+ lcdif, etm, usb, usb_phy,
+ clk_max
+};
+
+static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
+
+static enum imx23_clk clks_init_on[] __initdata = {
+ cpu, hbus, xbus, emi, uart,
+};
+
+int __init mx23_clocks_init(void)
+{
+ struct device_node *np;
+ u32 i;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl");
+ digctrl = of_iomap(np, 0);
+ WARN_ON(!digctrl);
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl");
+ clkctrl = of_iomap(np, 0);
+ WARN_ON(!clkctrl);
+
+ clk_misc_init();
+
+ clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000);
+ clks[pll] = mxs_clk_pll("pll", "ref_xtal", PLLCTRL0, 16, 480000000);
+ clks[ref_cpu] = mxs_clk_ref("ref_cpu", "pll", FRAC, 0);
+ clks[ref_emi] = mxs_clk_ref("ref_emi", "pll", FRAC, 1);
+ clks[ref_pix] = mxs_clk_ref("ref_pix", "pll", FRAC, 2);
+ clks[ref_io] = mxs_clk_ref("ref_io", "pll", FRAC, 3);
+ clks[saif_sel] = mxs_clk_mux("saif_sel", CLKSEQ, 0, 1, sel_pll, ARRAY_SIZE(sel_pll));
+ clks[lcdif_sel] = mxs_clk_mux("lcdif_sel", CLKSEQ, 1, 1, sel_pix, ARRAY_SIZE(sel_pix));
+ clks[gpmi_sel] = mxs_clk_mux("gpmi_sel", CLKSEQ, 4, 1, sel_io, ARRAY_SIZE(sel_io));
+ clks[ssp_sel] = mxs_clk_mux("ssp_sel", CLKSEQ, 5, 1, sel_io, ARRAY_SIZE(sel_io));
+ clks[emi_sel] = mxs_clk_mux("emi_sel", CLKSEQ, 6, 1, emi_sels, ARRAY_SIZE(emi_sels));
+ clks[cpu] = mxs_clk_mux("cpu", CLKSEQ, 7, 1, cpu_sels, ARRAY_SIZE(cpu_sels));
+ clks[etm_sel] = mxs_clk_mux("etm_sel", CLKSEQ, 8, 1, sel_cpu, ARRAY_SIZE(sel_cpu));
+ clks[cpu_pll] = mxs_clk_div("cpu_pll", "ref_cpu", CPU, 0, 6, 28);
+ clks[cpu_xtal] = mxs_clk_div("cpu_xtal", "ref_xtal", CPU, 16, 10, 29);
+ clks[hbus] = mxs_clk_div("hbus", "cpu", HBUS, 0, 5, 29);
+ clks[xbus] = mxs_clk_div("xbus", "ref_xtal", XBUS, 0, 10, 31);
+ clks[lcdif_div] = mxs_clk_div("lcdif_div", "lcdif_sel", PIX, 0, 12, 29);
+ clks[ssp_div] = mxs_clk_div("ssp_div", "ssp_sel", SSP, 0, 9, 29);
+ clks[gpmi_div] = mxs_clk_div("gpmi_div", "gpmi_sel", GPMI, 0, 10, 29);
+ clks[emi_pll] = mxs_clk_div("emi_pll", "ref_emi", EMI, 0, 6, 28);
+ clks[emi_xtal] = mxs_clk_div("emi_xtal", "ref_xtal", EMI, 8, 4, 29);
+ clks[etm_div] = mxs_clk_div("etm_div", "etm_sel", ETM, 0, 6, 29);
+ clks[saif_div] = mxs_clk_frac("saif_div", "saif_sel", SAIF, 0, 16, 29);
+ clks[clk32k_div] = mxs_clk_fixed_factor("clk32k_div", "ref_xtal", 1, 750);
+ clks[rtc] = mxs_clk_fixed_factor("rtc", "ref_xtal", 1, 768);
+ clks[adc] = mxs_clk_fixed_factor("adc", "clk32k", 1, 16);
+ clks[spdif_div] = mxs_clk_fixed_factor("spdif_div", "pll", 1, 4);
+ clks[clk32k] = mxs_clk_gate("clk32k", "clk32k_div", XTAL, 26);
+ clks[dri] = mxs_clk_gate("dri", "ref_xtal", XTAL, 28);
+ clks[pwm] = mxs_clk_gate("pwm", "ref_xtal", XTAL, 29);
+ clks[filt] = mxs_clk_gate("filt", "ref_xtal", XTAL, 30);
+ clks[uart] = mxs_clk_gate("uart", "ref_xtal", XTAL, 31);
+ clks[ssp] = mxs_clk_gate("ssp", "ssp_div", SSP, 31);
+ clks[gpmi] = mxs_clk_gate("gpmi", "gpmi_div", GPMI, 31);
+ clks[spdif] = mxs_clk_gate("spdif", "spdif_div", SPDIF, 31);
+ clks[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 31);
+ clks[saif] = mxs_clk_gate("saif", "saif_div", SAIF, 31);
+ clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", PIX, 31);
+ clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31);
+ clks[usb] = mxs_clk_gate("usb", "usb_phy", DIGCTRL, 2);
+ clks[usb_phy] = clk_register_gate(NULL, "usb_phy", "pll", 0, PLLCTRL0, 18, 0, &mxs_lock);
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++)
+ if (IS_ERR(clks[i])) {
+ pr_err("i.MX23 clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ return PTR_ERR(clks[i]);
+ }
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
+ clk_prepare_enable(clks[clks_init_on[i]]);
+
+ return 0;
+}
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
new file mode 100644
index 000000000..4faf0afc4
--- /dev/null
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk/mxs.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include "clk.h"
+
+static void __iomem *clkctrl;
+#define CLKCTRL clkctrl
+
+#define PLL0CTRL0 (CLKCTRL + 0x0000)
+#define PLL1CTRL0 (CLKCTRL + 0x0020)
+#define PLL2CTRL0 (CLKCTRL + 0x0040)
+#define CPU (CLKCTRL + 0x0050)
+#define HBUS (CLKCTRL + 0x0060)
+#define XBUS (CLKCTRL + 0x0070)
+#define XTAL (CLKCTRL + 0x0080)
+#define SSP0 (CLKCTRL + 0x0090)
+#define SSP1 (CLKCTRL + 0x00a0)
+#define SSP2 (CLKCTRL + 0x00b0)
+#define SSP3 (CLKCTRL + 0x00c0)
+#define GPMI (CLKCTRL + 0x00d0)
+#define SPDIF (CLKCTRL + 0x00e0)
+#define EMI (CLKCTRL + 0x00f0)
+#define SAIF0 (CLKCTRL + 0x0100)
+#define SAIF1 (CLKCTRL + 0x0110)
+#define LCDIF (CLKCTRL + 0x0120)
+#define ETM (CLKCTRL + 0x0130)
+#define ENET (CLKCTRL + 0x0140)
+#define FLEXCAN (CLKCTRL + 0x0160)
+#define FRAC0 (CLKCTRL + 0x01b0)
+#define FRAC1 (CLKCTRL + 0x01c0)
+#define CLKSEQ (CLKCTRL + 0x01d0)
+
+#define BP_CPU_INTERRUPT_WAIT 12
+#define BP_SAIF_DIV_FRAC_EN 16
+#define BP_ENET_DIV_TIME 21
+#define BP_ENET_SLEEP 31
+#define BP_CLKSEQ_BYPASS_SAIF0 0
+#define BP_CLKSEQ_BYPASS_SSP0 3
+#define BP_FRAC0_IO1FRAC 16
+#define BP_FRAC0_IO0FRAC 24
+
+static void __iomem *digctrl;
+#define DIGCTRL digctrl
+#define BP_SAIF_CLKMUX 10
+
+/*
+ * HW_SAIF_CLKMUX_SEL:
+ * DIRECT(0x0): SAIF0 clock pins selected for SAIF0 input clocks, and SAIF1
+ * clock pins selected for SAIF1 input clocks.
+ * CROSSINPUT(0x1): SAIF1 clock inputs selected for SAIF0 input clocks, and
+ * SAIF0 clock inputs selected for SAIF1 input clocks.
+ * EXTMSTR0(0x2): SAIF0 clock pin selected for both SAIF0 and SAIF1 input
+ * clocks.
+ * EXTMSTR1(0x3): SAIF1 clock pin selected for both SAIF0 and SAIF1 input
+ * clocks.
+ */
+int mxs_saif_clkmux_select(unsigned int clkmux)
+{
+ if (clkmux > 0x3)
+ return -EINVAL;
+
+ writel_relaxed(0x3 << BP_SAIF_CLKMUX, DIGCTRL + CLR);
+ writel_relaxed(clkmux << BP_SAIF_CLKMUX, DIGCTRL + SET);
+
+ return 0;
+}
+
+static void __init clk_misc_init(void)
+{
+ u32 val;
+
+ /* Gate off cpu clock in WFI for power saving */
+ writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET);
+
+ /* 0 is a bad default value for a divider */
+ writel_relaxed(1 << BP_ENET_DIV_TIME, ENET + SET);
+
+ /* Clear BYPASS for SAIF */
+ writel_relaxed(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ + CLR);
+
+ /* SAIF has to use frac div for functional operation */
+ val = readl_relaxed(SAIF0);
+ val |= 1 << BP_SAIF_DIV_FRAC_EN;
+ writel_relaxed(val, SAIF0);
+
+ val = readl_relaxed(SAIF1);
+ val |= 1 << BP_SAIF_DIV_FRAC_EN;
+ writel_relaxed(val, SAIF1);
+
+ /* Extra fec clock setting */
+ val = readl_relaxed(ENET);
+ val &= ~(1 << BP_ENET_SLEEP);
+ writel_relaxed(val, ENET);
+
+ /*
+ * Source ssp clock from ref_io than ref_xtal,
+ * as ref_xtal only provides 24 MHz as maximum.
+ */
+ writel_relaxed(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ + CLR);
+
+ /*
+ * 480 MHz seems too high to be ssp clock source directly,
+ * so set frac0 to get a 288 MHz ref_io0 and ref_io1.
+ */
+ val = readl_relaxed(FRAC0);
+ val &= ~((0x3f << BP_FRAC0_IO0FRAC) | (0x3f << BP_FRAC0_IO1FRAC));
+ val |= (30 << BP_FRAC0_IO0FRAC) | (30 << BP_FRAC0_IO1FRAC);
+ writel_relaxed(val, FRAC0);
+}
+
+static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", };
+static const char *sel_io0[] __initconst = { "ref_io0", "ref_xtal", };
+static const char *sel_io1[] __initconst = { "ref_io1", "ref_xtal", };
+static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", };
+static const char *sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", };
+static const char *sel_pll0[] __initconst = { "pll0", "ref_xtal", };
+static const char *cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", };
+static const char *emi_sels[] __initconst = { "emi_pll", "emi_xtal", };
+static const char *ptp_sels[] __initconst = { "ref_xtal", "pll0", };
+
+enum imx28_clk {
+ ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1,
+ ref_pix, ref_hsadc, ref_gpmi, saif0_sel, saif1_sel, gpmi_sel,
+ ssp0_sel, ssp1_sel, ssp2_sel, ssp3_sel, emi_sel, etm_sel,
+ lcdif_sel, cpu, ptp_sel, cpu_pll, cpu_xtal, hbus, xbus,
+ ssp0_div, ssp1_div, ssp2_div, ssp3_div, gpmi_div, emi_pll,
+ emi_xtal, lcdif_div, etm_div, ptp, saif0_div, saif1_div,
+ clk32k_div, rtc, lradc, spdif_div, clk32k, pwm, uart, ssp0,
+ ssp1, ssp2, ssp3, gpmi, spdif, emi, saif0, saif1, lcdif, etm,
+ fec, can0, can1, usb0, usb1, usb0_phy, usb1_phy, enet_out,
+ clk_max
+};
+
+static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
+
+static enum imx28_clk clks_init_on[] __initdata = {
+ cpu, hbus, xbus, emi, uart,
+};
+
+int __init mx28_clocks_init(void)
+{
+ struct device_node *np;
+ u32 i;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx28-digctl");
+ digctrl = of_iomap(np, 0);
+ WARN_ON(!digctrl);
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl");
+ clkctrl = of_iomap(np, 0);
+ WARN_ON(!clkctrl);
+
+ clk_misc_init();
+
+ clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000);
+ clks[pll0] = mxs_clk_pll("pll0", "ref_xtal", PLL0CTRL0, 17, 480000000);
+ clks[pll1] = mxs_clk_pll("pll1", "ref_xtal", PLL1CTRL0, 17, 480000000);
+ clks[pll2] = mxs_clk_pll("pll2", "ref_xtal", PLL2CTRL0, 23, 50000000);
+ clks[ref_cpu] = mxs_clk_ref("ref_cpu", "pll0", FRAC0, 0);
+ clks[ref_emi] = mxs_clk_ref("ref_emi", "pll0", FRAC0, 1);
+ clks[ref_io1] = mxs_clk_ref("ref_io1", "pll0", FRAC0, 2);
+ clks[ref_io0] = mxs_clk_ref("ref_io0", "pll0", FRAC0, 3);
+ clks[ref_pix] = mxs_clk_ref("ref_pix", "pll0", FRAC1, 0);
+ clks[ref_hsadc] = mxs_clk_ref("ref_hsadc", "pll0", FRAC1, 1);
+ clks[ref_gpmi] = mxs_clk_ref("ref_gpmi", "pll0", FRAC1, 2);
+ clks[saif0_sel] = mxs_clk_mux("saif0_sel", CLKSEQ, 0, 1, sel_pll0, ARRAY_SIZE(sel_pll0));
+ clks[saif1_sel] = mxs_clk_mux("saif1_sel", CLKSEQ, 1, 1, sel_pll0, ARRAY_SIZE(sel_pll0));
+ clks[gpmi_sel] = mxs_clk_mux("gpmi_sel", CLKSEQ, 2, 1, sel_gpmi, ARRAY_SIZE(sel_gpmi));
+ clks[ssp0_sel] = mxs_clk_mux("ssp0_sel", CLKSEQ, 3, 1, sel_io0, ARRAY_SIZE(sel_io0));
+ clks[ssp1_sel] = mxs_clk_mux("ssp1_sel", CLKSEQ, 4, 1, sel_io0, ARRAY_SIZE(sel_io0));
+ clks[ssp2_sel] = mxs_clk_mux("ssp2_sel", CLKSEQ, 5, 1, sel_io1, ARRAY_SIZE(sel_io1));
+ clks[ssp3_sel] = mxs_clk_mux("ssp3_sel", CLKSEQ, 6, 1, sel_io1, ARRAY_SIZE(sel_io1));
+ clks[emi_sel] = mxs_clk_mux("emi_sel", CLKSEQ, 7, 1, emi_sels, ARRAY_SIZE(emi_sels));
+ clks[etm_sel] = mxs_clk_mux("etm_sel", CLKSEQ, 8, 1, sel_cpu, ARRAY_SIZE(sel_cpu));
+ clks[lcdif_sel] = mxs_clk_mux("lcdif_sel", CLKSEQ, 14, 1, sel_pix, ARRAY_SIZE(sel_pix));
+ clks[cpu] = mxs_clk_mux("cpu", CLKSEQ, 18, 1, cpu_sels, ARRAY_SIZE(cpu_sels));
+ clks[ptp_sel] = mxs_clk_mux("ptp_sel", ENET, 19, 1, ptp_sels, ARRAY_SIZE(ptp_sels));
+ clks[cpu_pll] = mxs_clk_div("cpu_pll", "ref_cpu", CPU, 0, 6, 28);
+ clks[cpu_xtal] = mxs_clk_div("cpu_xtal", "ref_xtal", CPU, 16, 10, 29);
+ clks[hbus] = mxs_clk_div("hbus", "cpu", HBUS, 0, 5, 31);
+ clks[xbus] = mxs_clk_div("xbus", "ref_xtal", XBUS, 0, 10, 31);
+ clks[ssp0_div] = mxs_clk_div("ssp0_div", "ssp0_sel", SSP0, 0, 9, 29);
+ clks[ssp1_div] = mxs_clk_div("ssp1_div", "ssp1_sel", SSP1, 0, 9, 29);
+ clks[ssp2_div] = mxs_clk_div("ssp2_div", "ssp2_sel", SSP2, 0, 9, 29);
+ clks[ssp3_div] = mxs_clk_div("ssp3_div", "ssp3_sel", SSP3, 0, 9, 29);
+ clks[gpmi_div] = mxs_clk_div("gpmi_div", "gpmi_sel", GPMI, 0, 10, 29);
+ clks[emi_pll] = mxs_clk_div("emi_pll", "ref_emi", EMI, 0, 6, 28);
+ clks[emi_xtal] = mxs_clk_div("emi_xtal", "ref_xtal", EMI, 8, 4, 29);
+ clks[lcdif_div] = mxs_clk_div("lcdif_div", "lcdif_sel", LCDIF, 0, 13, 29);
+ clks[etm_div] = mxs_clk_div("etm_div", "etm_sel", ETM, 0, 7, 29);
+ clks[ptp] = mxs_clk_div("ptp", "ptp_sel", ENET, 21, 6, 27);
+ clks[saif0_div] = mxs_clk_frac("saif0_div", "saif0_sel", SAIF0, 0, 16, 29);
+ clks[saif1_div] = mxs_clk_frac("saif1_div", "saif1_sel", SAIF1, 0, 16, 29);
+ clks[clk32k_div] = mxs_clk_fixed_factor("clk32k_div", "ref_xtal", 1, 750);
+ clks[rtc] = mxs_clk_fixed_factor("rtc", "ref_xtal", 1, 768);
+ clks[lradc] = mxs_clk_fixed_factor("lradc", "clk32k", 1, 16);
+ clks[spdif_div] = mxs_clk_fixed_factor("spdif_div", "pll0", 1, 4);
+ clks[clk32k] = mxs_clk_gate("clk32k", "clk32k_div", XTAL, 26);
+ clks[pwm] = mxs_clk_gate("pwm", "ref_xtal", XTAL, 29);
+ clks[uart] = mxs_clk_gate("uart", "ref_xtal", XTAL, 31);
+ clks[ssp0] = mxs_clk_gate("ssp0", "ssp0_div", SSP0, 31);
+ clks[ssp1] = mxs_clk_gate("ssp1", "ssp1_div", SSP1, 31);
+ clks[ssp2] = mxs_clk_gate("ssp2", "ssp2_div", SSP2, 31);
+ clks[ssp3] = mxs_clk_gate("ssp3", "ssp3_div", SSP3, 31);
+ clks[gpmi] = mxs_clk_gate("gpmi", "gpmi_div", GPMI, 31);
+ clks[spdif] = mxs_clk_gate("spdif", "spdif_div", SPDIF, 31);
+ clks[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 31);
+ clks[saif0] = mxs_clk_gate("saif0", "saif0_div", SAIF0, 31);
+ clks[saif1] = mxs_clk_gate("saif1", "saif1_div", SAIF1, 31);
+ clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", LCDIF, 31);
+ clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31);
+ clks[fec] = mxs_clk_gate("fec", "hbus", ENET, 30);
+ clks[can0] = mxs_clk_gate("can0", "ref_xtal", FLEXCAN, 30);
+ clks[can1] = mxs_clk_gate("can1", "ref_xtal", FLEXCAN, 28);
+ clks[usb0] = mxs_clk_gate("usb0", "usb0_phy", DIGCTRL, 2);
+ clks[usb1] = mxs_clk_gate("usb1", "usb1_phy", DIGCTRL, 16);
+ clks[usb0_phy] = clk_register_gate(NULL, "usb0_phy", "pll0", 0, PLL0CTRL0, 18, 0, &mxs_lock);
+ clks[usb1_phy] = clk_register_gate(NULL, "usb1_phy", "pll1", 0, PLL1CTRL0, 18, 0, &mxs_lock);
+ clks[enet_out] = clk_register_gate(NULL, "enet_out", "pll2", 0, ENET, 18, 0, &mxs_lock);
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++)
+ if (IS_ERR(clks[i])) {
+ pr_err("i.MX28 clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ return PTR_ERR(clks[i]);
+ }
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ clk_register_clkdev(clks[enet_out], NULL, "enet_out");
+
+ for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
+ clk_prepare_enable(clks[clks_init_on[i]]);
+
+ return 0;
+}
diff --git a/drivers/clk/mxs/clk-pll.c b/drivers/clk/mxs/clk-pll.c
new file mode 100644
index 000000000..fadae4183
--- /dev/null
+++ b/drivers/clk/mxs/clk-pll.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+/**
+ * struct clk_pll - mxs pll clock
+ * @hw: clk_hw for the pll
+ * @base: base address of the pll
+ * @power: the shift of power bit
+ * @rate: the clock rate of the pll
+ *
+ * The mxs pll is a fixed rate clock with power and gate control,
+ * and the shift of gate bit is always 31.
+ */
+struct clk_pll {
+ struct clk_hw hw;
+ void __iomem *base;
+ u8 power;
+ unsigned long rate;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+static int clk_pll_prepare(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+
+ writel_relaxed(1 << pll->power, pll->base + SET);
+
+ udelay(10);
+
+ return 0;
+}
+
+static void clk_pll_unprepare(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+
+ writel_relaxed(1 << pll->power, pll->base + CLR);
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+
+ writel_relaxed(1 << 31, pll->base + CLR);
+
+ return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+
+ writel_relaxed(1 << 31, pll->base + SET);
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+
+ return pll->rate;
+}
+
+static const struct clk_ops clk_pll_ops = {
+ .prepare = clk_pll_prepare,
+ .unprepare = clk_pll_unprepare,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+struct clk *mxs_clk_pll(const char *name, const char *parent_name,
+ void __iomem *base, u8 power, unsigned long rate)
+{
+ struct clk_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_pll_ops;
+ init.flags = 0;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ pll->base = base;
+ pll->rate = rate;
+ pll->power = power;
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
diff --git a/drivers/clk/mxs/clk-ref.c b/drivers/clk/mxs/clk-ref.c
new file mode 100644
index 000000000..4adeed6c2
--- /dev/null
+++ b/drivers/clk/mxs/clk-ref.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+/**
+ * struct clk_ref - mxs reference clock
+ * @hw: clk_hw for the reference clock
+ * @reg: register address
+ * @idx: the index of the reference clock within the same register
+ *
+ * The mxs reference clock sources from pll. Every 4 reference clocks share
+ * one register space, and @idx is used to identify them. Each reference
+ * clock has a gate control and a fractional * divider. The rate is calculated
+ * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35.
+ */
+struct clk_ref {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 idx;
+};
+
+#define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw)
+
+static int clk_ref_enable(struct clk_hw *hw)
+{
+ struct clk_ref *ref = to_clk_ref(hw);
+
+ writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR);
+
+ return 0;
+}
+
+static void clk_ref_disable(struct clk_hw *hw)
+{
+ struct clk_ref *ref = to_clk_ref(hw);
+
+ writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET);
+}
+
+static unsigned long clk_ref_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_ref *ref = to_clk_ref(hw);
+ u64 tmp = parent_rate;
+ u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f;
+
+ tmp *= 18;
+ do_div(tmp, frac);
+
+ return tmp;
+}
+
+static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ unsigned long parent_rate = *prate;
+ u64 tmp = parent_rate;
+ u8 frac;
+
+ tmp = tmp * 18 + rate / 2;
+ do_div(tmp, rate);
+ frac = tmp;
+
+ if (frac < 18)
+ frac = 18;
+ else if (frac > 35)
+ frac = 35;
+
+ tmp = parent_rate;
+ tmp *= 18;
+ do_div(tmp, frac);
+
+ return tmp;
+}
+
+static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_ref *ref = to_clk_ref(hw);
+ unsigned long flags;
+ u64 tmp = parent_rate;
+ u32 val;
+ u8 frac, shift = ref->idx * 8;
+
+ tmp = tmp * 18 + rate / 2;
+ do_div(tmp, rate);
+ frac = tmp;
+
+ if (frac < 18)
+ frac = 18;
+ else if (frac > 35)
+ frac = 35;
+
+ spin_lock_irqsave(&mxs_lock, flags);
+
+ val = readl_relaxed(ref->reg);
+ val &= ~(0x3f << shift);
+ val |= frac << shift;
+ writel_relaxed(val, ref->reg);
+
+ spin_unlock_irqrestore(&mxs_lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops clk_ref_ops = {
+ .enable = clk_ref_enable,
+ .disable = clk_ref_disable,
+ .recalc_rate = clk_ref_recalc_rate,
+ .round_rate = clk_ref_round_rate,
+ .set_rate = clk_ref_set_rate,
+};
+
+struct clk *mxs_clk_ref(const char *name, const char *parent_name,
+ void __iomem *reg, u8 idx)
+{
+ struct clk_ref *ref;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_ref_ops;
+ init.flags = 0;
+ init.parent_names = (parent_name ? &parent_name: NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ ref->reg = reg;
+ ref->idx = idx;
+ ref->hw.init = &init;
+
+ clk = clk_register(NULL, &ref->hw);
+ if (IS_ERR(clk))
+ kfree(ref);
+
+ return clk;
+}
diff --git a/drivers/clk/mxs/clk-ssp.c b/drivers/clk/mxs/clk-ssp.c
new file mode 100644
index 000000000..af7bdbf9e
--- /dev/null
+++ b/drivers/clk/mxs/clk-ssp.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 DENX Software Engineering, GmbH
+ *
+ * Pulled from code:
+ * Portions copyright (C) 2003 Russell King, PXA MMCI Driver
+ * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
+ *
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/spi/mxs-spi.h>
+
+void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate)
+{
+ unsigned int ssp_clk, ssp_sck;
+ u32 clock_divide, clock_rate;
+ u32 val;
+
+ ssp_clk = clk_get_rate(ssp->clk);
+
+ for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) {
+ clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide);
+ clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0;
+ if (clock_rate <= 255)
+ break;
+ }
+
+ if (clock_divide > 254) {
+ dev_err(ssp->dev,
+ "%s: cannot set clock to %d\n", __func__, rate);
+ return;
+ }
+
+ ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
+
+ val = readl(ssp->base + HW_SSP_TIMING(ssp));
+ val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
+ val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
+ val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
+ writel(val, ssp->base + HW_SSP_TIMING(ssp));
+
+ ssp->clk_rate = ssp_sck;
+
+ dev_dbg(ssp->dev,
+ "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n",
+ __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate);
+}
+EXPORT_SYMBOL_GPL(mxs_ssp_set_clk_rate);
diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c
new file mode 100644
index 000000000..5301bce89
--- /dev/null
+++ b/drivers/clk/mxs/clk.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include "clk.h"
+
+DEFINE_SPINLOCK(mxs_lock);
+
+int mxs_clk_wait(void __iomem *reg, u8 shift)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(10);
+
+ while (readl_relaxed(reg) & (1 << shift))
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ return 0;
+}
diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h
new file mode 100644
index 000000000..81421e28e
--- /dev/null
+++ b/drivers/clk/mxs/clk.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __MXS_CLK_H
+#define __MXS_CLK_H
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+#define SET 0x4
+#define CLR 0x8
+
+extern spinlock_t mxs_lock;
+
+int mxs_clk_wait(void __iomem *reg, u8 shift);
+
+struct clk *mxs_clk_pll(const char *name, const char *parent_name,
+ void __iomem *base, u8 power, unsigned long rate);
+
+struct clk *mxs_clk_ref(const char *name, const char *parent_name,
+ void __iomem *reg, u8 idx);
+
+struct clk *mxs_clk_div(const char *name, const char *parent_name,
+ void __iomem *reg, u8 shift, u8 width, u8 busy);
+
+struct clk *mxs_clk_frac(const char *name, const char *parent_name,
+ void __iomem *reg, u8 shift, u8 width, u8 busy);
+
+static inline struct clk *mxs_clk_fixed(const char *name, int rate)
+{
+ return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate);
+}
+
+static inline struct clk *mxs_clk_gate(const char *name,
+ const char *parent_name, void __iomem *reg, u8 shift)
+{
+ return clk_register_gate(NULL, name, parent_name, CLK_SET_RATE_PARENT,
+ reg, shift, CLK_GATE_SET_TO_DISABLE,
+ &mxs_lock);
+}
+
+static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg,
+ u8 shift, u8 width, const char **parent_names, int num_parents)
+{
+ return clk_register_mux(NULL, name, parent_names, num_parents,
+ CLK_SET_RATE_PARENT, reg, shift, width,
+ 0, &mxs_lock);
+}
+
+static inline struct clk *mxs_clk_fixed_factor(const char *name,
+ const char *parent_name, unsigned int mult, unsigned int div)
+{
+ return clk_register_fixed_factor(NULL, name, parent_name,
+ CLK_SET_RATE_PARENT, mult, div);
+}
+
+#endif /* __MXS_CLK_H */
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
new file mode 100644
index 000000000..b7c232e67
--- /dev/null
+++ b/drivers/clk/samsung/Makefile
@@ -0,0 +1,8 @@
+#
+# Samsung Clock specific Makefile
+#
+
+obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o
+obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o
+obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
+obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
new file mode 100644
index 000000000..b4b283b47
--- /dev/null
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -0,0 +1,1084 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab@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 all Exynos4 SoCs.
+*/
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk.h"
+#include "clk-pll.h"
+
+/* Exynos4 clock controller register offsets */
+#define SRC_LEFTBUS 0x4200
+#define DIV_LEFTBUS 0x4500
+#define GATE_IP_LEFTBUS 0x4800
+#define E4X12_GATE_IP_IMAGE 0x4930
+#define SRC_RIGHTBUS 0x8200
+#define DIV_RIGHTBUS 0x8500
+#define GATE_IP_RIGHTBUS 0x8800
+#define E4X12_GATE_IP_PERIR 0x8960
+#define EPLL_LOCK 0xc010
+#define VPLL_LOCK 0xc020
+#define EPLL_CON0 0xc110
+#define EPLL_CON1 0xc114
+#define EPLL_CON2 0xc118
+#define VPLL_CON0 0xc120
+#define VPLL_CON1 0xc124
+#define VPLL_CON2 0xc128
+#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 E4210_SRC_IMAGE 0xc230
+#define SRC_LCD0 0xc234
+#define E4210_SRC_LCD1 0xc238
+#define E4X12_SRC_ISP 0xc238
+#define SRC_MAUDIO 0xc23c
+#define SRC_FSYS 0xc240
+#define SRC_PERIL0 0xc250
+#define SRC_PERIL1 0xc254
+#define E4X12_SRC_CAM1 0xc258
+#define SRC_MASK_TOP 0xc310
+#define SRC_MASK_CAM 0xc320
+#define SRC_MASK_TV 0xc324
+#define SRC_MASK_LCD0 0xc334
+#define E4210_SRC_MASK_LCD1 0xc338
+#define E4X12_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_IMAGE 0xc530
+#define DIV_LCD0 0xc534
+#define E4210_DIV_LCD1 0xc538
+#define E4X12_DIV_ISP 0xc538
+#define DIV_MAUDIO 0xc53c
+#define DIV_FSYS0 0xc540
+#define DIV_FSYS1 0xc544
+#define DIV_FSYS2 0xc548
+#define DIV_FSYS3 0xc54c
+#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 E4X12_DIV_CAM1 0xc568
+#define GATE_SCLK_CAM 0xc820
+#define GATE_IP_CAM 0xc920
+#define GATE_IP_TV 0xc924
+#define GATE_IP_MFC 0xc928
+#define GATE_IP_G3D 0xc92c
+#define E4210_GATE_IP_IMAGE 0xc930
+#define GATE_IP_LCD0 0xc934
+#define E4210_GATE_IP_LCD1 0xc938
+#define E4X12_GATE_IP_ISP 0xc938
+#define E4X12_GATE_IP_MAUDIO 0xc93c
+#define GATE_IP_FSYS 0xc940
+#define GATE_IP_GPS 0xc94c
+#define GATE_IP_PERIL 0xc950
+#define E4210_GATE_IP_PERIR 0xc960
+#define GATE_BLOCK 0xc970
+#define E4X12_MPLL_CON0 0x10108
+#define SRC_DMC 0x10200
+#define SRC_MASK_DMC 0x10300
+#define DIV_DMC0 0x10500
+#define DIV_DMC1 0x10504
+#define GATE_IP_DMC 0x10900
+#define APLL_CON0 0x14100
+#define E4210_MPLL_CON0 0x14108
+#define SRC_CPU 0x14200
+#define DIV_CPU0 0x14500
+#define DIV_CPU1 0x14504
+#define GATE_SCLK_CPU 0x14800
+#define GATE_IP_CPU 0x14900
+#define E4X12_DIV_ISP0 0x18300
+#define E4X12_DIV_ISP1 0x18304
+#define E4X12_GATE_ISP0 0x18800
+#define E4X12_GATE_ISP1 0x18804
+
+/* the exynos4 soc type */
+enum exynos4_soc {
+ EXYNOS4210,
+ EXYNOS4X12,
+};
+
+/*
+ * Let each supported clock get a unique id. This id is used to lookup the clock
+ * for device tree based platforms. The clocks are categorized into three
+ * sections: core, sclk gate and bus interface gate clocks.
+ *
+ * When adding a new clock to this list, it is advised to choose a clock
+ * category and add it to the end of that category. That is because the the
+ * device tree source file is referring to these ids and any change in the
+ * sequence number of existing clocks will require corresponding change in the
+ * device tree files. This limitation would go away when pre-processor support
+ * for dtc would be available.
+ */
+enum exynos4_clks {
+ none,
+
+ /* core clocks */
+ xxti, xusbxti, fin_pll, fout_apll, fout_mpll, fout_epll, fout_vpll,
+ sclk_apll, sclk_mpll, sclk_epll, sclk_vpll, arm_clk, aclk200, aclk100,
+ aclk160, aclk133, mout_mpll_user_t, mout_mpll_user_c, mout_core,
+ mout_apll, /* 20 */
+
+ /* gate for special clocks (sclk) */
+ sclk_fimc0 = 128, sclk_fimc1, sclk_fimc2, sclk_fimc3, sclk_cam0,
+ sclk_cam1, sclk_csis0, sclk_csis1, sclk_hdmi, sclk_mixer, sclk_dac,
+ sclk_pixel, sclk_fimd0, sclk_mdnie0, sclk_mdnie_pwm0, sclk_mipi0,
+ sclk_audio0, sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_mmc4,
+ sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4,
+ sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2,
+ sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1,
+ sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, sclk_g3d, sclk_pwm_isp,
+ sclk_spi0_isp, sclk_spi1_isp, sclk_uart_isp,
+
+ /* gate clocks */
+ fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0,
+ smmu_fimc1, smmu_fimc2, smmu_fimc3, smmu_jpeg, vp, mixer, tvenc, hdmi,
+ smmu_tv, mfc, smmu_mfcl, smmu_mfcr, g3d, g2d, rotator, mdma, smmu_g2d,
+ smmu_rotator, smmu_mdma, fimd0, mie0, mdnie0, dsim0, smmu_fimd0, fimd1,
+ mie1, dsim1, smmu_fimd1, pdma0, pdma1, pcie_phy, sata_phy, tsi, sdmmc0,
+ sdmmc1, sdmmc2, sdmmc3, sdmmc4, sata, sromc, usb_host, usb_device, pcie,
+ onenand, nfcon, smmu_pcie, gps, smmu_gps, uart0, uart1, uart2, uart3,
+ uart4, i2c0, i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, tsadc,
+ spi0, spi1, spi2, i2s1, i2s2, pcm0, i2s0, pcm1, pcm2, pwm, slimbus,
+ spdif, ac97, modemif, chipid, sysreg, hdmi_cec, mct, wdt, rtc, keyif,
+ audss, mipi_hsi, mdma2, pixelasyncm0, pixelasyncm1, fimc_lite0,
+ fimc_lite1, ppmuispx, ppmuispmx, fimc_isp, fimc_drc, fimc_fd, mcuisp,
+ gicisp, smmu_isp, smmu_drc, smmu_fd, smmu_lite0, smmu_lite1, mcuctl_isp,
+ mpwm_isp, i2c0_isp, i2c1_isp, mtcadc_isp, pwm_isp, wdt_isp, uart_isp,
+ asyncaxim, smmu_ispcx, spi0_isp, spi1_isp, pwm_isp_sclk, spi0_isp_sclk,
+ spi1_isp_sclk, uart_isp_sclk,
+
+ /* mux clocks */
+ mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0,
+ mout_cam1, mout_csis0, mout_csis1, mout_g3d0, mout_g3d1, mout_g3d,
+ aclk400_mcuisp,
+
+ /* div clocks */
+ div_isp0 = 450, div_isp1, div_mcuisp0, div_mcuisp1, div_aclk200,
+ div_aclk400_mcuisp,
+
+ nr_clks,
+};
+
+/*
+ * list of controller registers to be saved and restored during a
+ * suspend/resume cycle.
+ */
+static __initdata unsigned long exynos4210_clk_save[] = {
+ E4210_SRC_IMAGE,
+ E4210_SRC_LCD1,
+ E4210_SRC_MASK_LCD1,
+ E4210_DIV_LCD1,
+ E4210_GATE_IP_IMAGE,
+ E4210_GATE_IP_LCD1,
+ E4210_GATE_IP_PERIR,
+ E4210_MPLL_CON0,
+};
+
+static __initdata unsigned long exynos4x12_clk_save[] = {
+ E4X12_GATE_IP_IMAGE,
+ E4X12_GATE_IP_PERIR,
+ E4X12_SRC_CAM1,
+ E4X12_DIV_ISP,
+ E4X12_DIV_CAM1,
+ E4X12_MPLL_CON0,
+};
+
+static __initdata unsigned long exynos4_clk_regs[] = {
+ SRC_LEFTBUS,
+ DIV_LEFTBUS,
+ GATE_IP_LEFTBUS,
+ SRC_RIGHTBUS,
+ DIV_RIGHTBUS,
+ GATE_IP_RIGHTBUS,
+ EPLL_CON0,
+ EPLL_CON1,
+ EPLL_CON2,
+ VPLL_CON0,
+ VPLL_CON1,
+ VPLL_CON2,
+ SRC_TOP0,
+ SRC_TOP1,
+ SRC_CAM,
+ SRC_TV,
+ SRC_MFC,
+ SRC_G3D,
+ SRC_LCD0,
+ SRC_MAUDIO,
+ SRC_FSYS,
+ SRC_PERIL0,
+ SRC_PERIL1,
+ SRC_MASK_TOP,
+ SRC_MASK_CAM,
+ SRC_MASK_TV,
+ SRC_MASK_LCD0,
+ SRC_MASK_MAUDIO,
+ SRC_MASK_FSYS,
+ SRC_MASK_PERIL0,
+ SRC_MASK_PERIL1,
+ DIV_TOP,
+ DIV_CAM,
+ DIV_TV,
+ DIV_MFC,
+ DIV_G3D,
+ DIV_IMAGE,
+ DIV_LCD0,
+ DIV_MAUDIO,
+ DIV_FSYS0,
+ DIV_FSYS1,
+ DIV_FSYS2,
+ DIV_FSYS3,
+ DIV_PERIL0,
+ DIV_PERIL1,
+ DIV_PERIL2,
+ DIV_PERIL3,
+ DIV_PERIL4,
+ DIV_PERIL5,
+ GATE_SCLK_CAM,
+ GATE_IP_CAM,
+ GATE_IP_TV,
+ GATE_IP_MFC,
+ GATE_IP_G3D,
+ GATE_IP_LCD0,
+ GATE_IP_FSYS,
+ GATE_IP_GPS,
+ GATE_IP_PERIL,
+ GATE_BLOCK,
+ SRC_MASK_DMC,
+ SRC_DMC,
+ DIV_DMC0,
+ DIV_DMC1,
+ GATE_IP_DMC,
+ APLL_CON0,
+ SRC_CPU,
+ DIV_CPU0,
+ DIV_CPU1,
+ GATE_SCLK_CPU,
+ GATE_IP_CPU,
+};
+
+/* list of all parent clock list */
+PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
+PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", };
+PNAME(mout_epll_p) = { "fin_pll", "fout_epll", };
+PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi24m", };
+PNAME(mout_vpll_p) = { "fin_pll", "fout_vpll", };
+PNAME(sclk_evpll_p) = { "sclk_epll", "sclk_vpll", };
+PNAME(mout_mfc_p) = { "mout_mfc0", "mout_mfc1", };
+PNAME(mout_g3d_p) = { "mout_g3d0", "mout_g3d1", };
+PNAME(mout_g2d_p) = { "mout_g2d0", "mout_g2d1", };
+PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy", };
+PNAME(mout_jpeg_p) = { "mout_jpeg0", "mout_jpeg1", };
+PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2",
+ "spdif_extclk", };
+PNAME(mout_onenand_p) = {"aclk133", "aclk160", };
+PNAME(mout_onenand1_p) = {"mout_onenand", "sclk_vpll", };
+
+/* Exynos 4210-specific parent groups */
+PNAME(sclk_vpll_p4210) = { "mout_vpllsrc", "fout_vpll", };
+PNAME(mout_core_p4210) = { "mout_apll", "sclk_mpll", };
+PNAME(sclk_ampll_p4210) = { "sclk_mpll", "sclk_apll", };
+PNAME(group1_p4210) = { "xxti", "xusbxti", "sclk_hdmi24m",
+ "sclk_usbphy0", "none", "sclk_hdmiphy",
+ "sclk_mpll", "sclk_epll", "sclk_vpll", };
+PNAME(mout_audio0_p4210) = { "cdclk0", "none", "sclk_hdmi24m",
+ "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll",
+ "sclk_epll", "sclk_vpll" };
+PNAME(mout_audio1_p4210) = { "cdclk1", "none", "sclk_hdmi24m",
+ "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll",
+ "sclk_epll", "sclk_vpll", };
+PNAME(mout_audio2_p4210) = { "cdclk2", "none", "sclk_hdmi24m",
+ "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll",
+ "sclk_epll", "sclk_vpll", };
+PNAME(mout_mixer_p4210) = { "sclk_dac", "sclk_hdmi", };
+PNAME(mout_dac_p4210) = { "sclk_vpll", "sclk_hdmiphy", };
+
+/* Exynos 4x12-specific parent groups */
+PNAME(mout_mpll_user_p4x12) = { "fin_pll", "sclk_mpll", };
+PNAME(mout_core_p4x12) = { "mout_apll", "mout_mpll_user_c", };
+PNAME(sclk_ampll_p4x12) = { "mout_mpll_user_t", "sclk_apll", };
+PNAME(group1_p4x12) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0",
+ "none", "sclk_hdmiphy", "mout_mpll_user_t",
+ "sclk_epll", "sclk_vpll", };
+PNAME(mout_audio0_p4x12) = { "cdclk0", "none", "sclk_hdmi24m",
+ "sclk_usbphy0", "xxti", "xusbxti",
+ "mout_mpll_user_t", "sclk_epll", "sclk_vpll" };
+PNAME(mout_audio1_p4x12) = { "cdclk1", "none", "sclk_hdmi24m",
+ "sclk_usbphy0", "xxti", "xusbxti",
+ "mout_mpll_user_t", "sclk_epll", "sclk_vpll", };
+PNAME(mout_audio2_p4x12) = { "cdclk2", "none", "sclk_hdmi24m",
+ "sclk_usbphy0", "xxti", "xusbxti",
+ "mout_mpll_user_t", "sclk_epll", "sclk_vpll", };
+PNAME(aclk_p4412) = { "mout_mpll_user_t", "sclk_apll", };
+PNAME(mout_user_aclk400_mcuisp_p4x12) = {"fin_pll", "div_aclk400_mcuisp", };
+PNAME(mout_user_aclk200_p4x12) = {"fin_pll", "div_aclk200", };
+PNAME(mout_user_aclk266_gps_p4x12) = {"fin_pll", "div_aclk266_gps", };
+
+/* fixed rate clocks generated outside the soc */
+struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata = {
+ FRATE(xxti, "xxti", NULL, CLK_IS_ROOT, 0),
+ FRATE(xusbxti, "xusbxti", NULL, CLK_IS_ROOT, 0),
+};
+
+/* fixed rate clocks generated inside the soc */
+struct samsung_fixed_rate_clock exynos4_fixed_rate_clks[] __initdata = {
+ FRATE(none, "sclk_hdmi24m", NULL, CLK_IS_ROOT, 24000000),
+ FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000),
+ FRATE(none, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000),
+};
+
+struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = {
+ FRATE(none, "sclk_usbphy1", NULL, CLK_IS_ROOT, 48000000),
+};
+
+/* list of mux clocks supported in all exynos4 soc's */
+struct samsung_mux_clock exynos4_mux_clks[] __initdata = {
+ MUX_F(mout_apll, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
+ CLK_SET_RATE_PARENT, 0),
+ MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1),
+ MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1),
+ MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1),
+ MUX_F(mout_g3d1, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1,
+ CLK_SET_RATE_PARENT, 0),
+ MUX_F(mout_g3d, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1,
+ CLK_SET_RATE_PARENT, 0),
+ MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIL1, 8, 2),
+ MUX(none, "mout_onenand1", mout_onenand1_p, SRC_TOP0, 0, 1),
+ MUX_A(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1, "sclk_epll"),
+ MUX(none, "mout_onenand", mout_onenand_p, SRC_TOP0, 28, 1),
+};
+
+/* list of mux clocks supported in exynos4210 soc */
+struct samsung_mux_clock exynos4210_mux_clks[] __initdata = {
+ MUX(none, "mout_aclk200", sclk_ampll_p4210, SRC_TOP0, 12, 1),
+ MUX(none, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1),
+ MUX(none, "mout_aclk160", sclk_ampll_p4210, SRC_TOP0, 20, 1),
+ MUX(none, "mout_aclk133", sclk_ampll_p4210, SRC_TOP0, 24, 1),
+ MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP1, 0, 1),
+ MUX(none, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1),
+ MUX(none, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1),
+ MUX(none, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1),
+ MUX(none, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1),
+ MUX(none, "mout_g2d", mout_g2d_p, E4210_SRC_IMAGE, 8, 1),
+ MUX(none, "mout_fimd1", group1_p4210, E4210_SRC_LCD1, 0, 4),
+ MUX(none, "mout_mipi1", group1_p4210, E4210_SRC_LCD1, 12, 4),
+ MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"),
+ MUX_A(mout_core, "mout_core", mout_core_p4210,
+ SRC_CPU, 16, 1, "mout_core"),
+ MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210,
+ SRC_TOP0, 8, 1, "sclk_vpll"),
+ MUX(mout_fimc0, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4),
+ MUX(mout_fimc1, "mout_fimc1", group1_p4210, SRC_CAM, 4, 4),
+ MUX(mout_fimc2, "mout_fimc2", group1_p4210, SRC_CAM, 8, 4),
+ MUX(mout_fimc3, "mout_fimc3", group1_p4210, SRC_CAM, 12, 4),
+ MUX(mout_cam0, "mout_cam0", group1_p4210, SRC_CAM, 16, 4),
+ MUX(mout_cam1, "mout_cam1", group1_p4210, SRC_CAM, 20, 4),
+ MUX(mout_csis0, "mout_csis0", group1_p4210, SRC_CAM, 24, 4),
+ MUX(mout_csis1, "mout_csis1", group1_p4210, SRC_CAM, 28, 4),
+ MUX(none, "mout_mfc0", sclk_ampll_p4210, SRC_MFC, 0, 1),
+ MUX_F(mout_g3d0, "mout_g3d0", sclk_ampll_p4210, SRC_G3D, 0, 1,
+ CLK_SET_RATE_PARENT, 0),
+ MUX(none, "mout_fimd0", group1_p4210, SRC_LCD0, 0, 4),
+ MUX(none, "mout_mipi0", group1_p4210, SRC_LCD0, 12, 4),
+ MUX(none, "mout_audio0", mout_audio0_p4210, SRC_MAUDIO, 0, 4),
+ MUX(none, "mout_mmc0", group1_p4210, SRC_FSYS, 0, 4),
+ MUX(none, "mout_mmc1", group1_p4210, SRC_FSYS, 4, 4),
+ MUX(none, "mout_mmc2", group1_p4210, SRC_FSYS, 8, 4),
+ MUX(none, "mout_mmc3", group1_p4210, SRC_FSYS, 12, 4),
+ MUX(none, "mout_mmc4", group1_p4210, SRC_FSYS, 16, 4),
+ MUX(none, "mout_sata", sclk_ampll_p4210, SRC_FSYS, 24, 1),
+ MUX(none, "mout_uart0", group1_p4210, SRC_PERIL0, 0, 4),
+ MUX(none, "mout_uart1", group1_p4210, SRC_PERIL0, 4, 4),
+ MUX(none, "mout_uart2", group1_p4210, SRC_PERIL0, 8, 4),
+ MUX(none, "mout_uart3", group1_p4210, SRC_PERIL0, 12, 4),
+ MUX(none, "mout_uart4", group1_p4210, SRC_PERIL0, 16, 4),
+ MUX(none, "mout_audio1", mout_audio1_p4210, SRC_PERIL1, 0, 4),
+ MUX(none, "mout_audio2", mout_audio2_p4210, SRC_PERIL1, 4, 4),
+ MUX(none, "mout_spi0", group1_p4210, SRC_PERIL1, 16, 4),
+ MUX(none, "mout_spi1", group1_p4210, SRC_PERIL1, 20, 4),
+ MUX(none, "mout_spi2", group1_p4210, SRC_PERIL1, 24, 4),
+};
+
+/* list of mux clocks supported in exynos4x12 soc */
+struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = {
+ MUX(mout_mpll_user_c, "mout_mpll_user_c", mout_mpll_user_p4x12,
+ SRC_CPU, 24, 1),
+ MUX(none, "mout_aclk266_gps", aclk_p4412, SRC_TOP1, 4, 1),
+ MUX(none, "mout_aclk400_mcuisp", aclk_p4412, SRC_TOP1, 8, 1),
+ MUX(mout_mpll_user_t, "mout_mpll_user_t", mout_mpll_user_p4x12,
+ SRC_TOP1, 12, 1),
+ MUX(none, "mout_user_aclk266_gps", mout_user_aclk266_gps_p4x12,
+ SRC_TOP1, 16, 1),
+ MUX(aclk200, "aclk200", mout_user_aclk200_p4x12, SRC_TOP1, 20, 1),
+ MUX(aclk400_mcuisp, "aclk400_mcuisp", mout_user_aclk400_mcuisp_p4x12,
+ SRC_TOP1, 24, 1),
+ MUX(none, "mout_aclk200", aclk_p4412, SRC_TOP0, 12, 1),
+ MUX(none, "mout_aclk100", aclk_p4412, SRC_TOP0, 16, 1),
+ MUX(none, "mout_aclk160", aclk_p4412, SRC_TOP0, 20, 1),
+ MUX(none, "mout_aclk133", aclk_p4412, SRC_TOP0, 24, 1),
+ MUX(none, "mout_mdnie0", group1_p4x12, SRC_LCD0, 4, 4),
+ MUX(none, "mout_mdnie_pwm0", group1_p4x12, SRC_LCD0, 8, 4),
+ MUX(none, "mout_sata", sclk_ampll_p4x12, SRC_FSYS, 24, 1),
+ MUX(none, "mout_jpeg0", sclk_ampll_p4x12, E4X12_SRC_CAM1, 0, 1),
+ MUX(none, "mout_jpeg1", sclk_evpll_p, E4X12_SRC_CAM1, 4, 1),
+ MUX(none, "mout_jpeg", mout_jpeg_p, E4X12_SRC_CAM1, 8, 1),
+ MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p,
+ SRC_DMC, 12, 1, "sclk_mpll"),
+ MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p,
+ SRC_TOP0, 8, 1, "sclk_vpll"),
+ MUX(mout_core, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1),
+ MUX(mout_fimc0, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4),
+ MUX(mout_fimc1, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4),
+ MUX(mout_fimc2, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4),
+ MUX(mout_fimc3, "mout_fimc3", group1_p4x12, SRC_CAM, 12, 4),
+ MUX(mout_cam0, "mout_cam0", group1_p4x12, SRC_CAM, 16, 4),
+ MUX(mout_cam1, "mout_cam1", group1_p4x12, SRC_CAM, 20, 4),
+ MUX(mout_csis0, "mout_csis0", group1_p4x12, SRC_CAM, 24, 4),
+ MUX(mout_csis1, "mout_csis1", group1_p4x12, SRC_CAM, 28, 4),
+ MUX(none, "mout_mfc0", sclk_ampll_p4x12, SRC_MFC, 0, 1),
+ MUX_F(mout_g3d0, "mout_g3d0", sclk_ampll_p4x12, SRC_G3D, 0, 1,
+ CLK_SET_RATE_PARENT, 0),
+ MUX(none, "mout_fimd0", group1_p4x12, SRC_LCD0, 0, 4),
+ MUX(none, "mout_mipi0", group1_p4x12, SRC_LCD0, 12, 4),
+ MUX(none, "mout_audio0", mout_audio0_p4x12, SRC_MAUDIO, 0, 4),
+ MUX(none, "mout_mmc0", group1_p4x12, SRC_FSYS, 0, 4),
+ MUX(none, "mout_mmc1", group1_p4x12, SRC_FSYS, 4, 4),
+ MUX(none, "mout_mmc2", group1_p4x12, SRC_FSYS, 8, 4),
+ MUX(none, "mout_mmc3", group1_p4x12, SRC_FSYS, 12, 4),
+ MUX(none, "mout_mmc4", group1_p4x12, SRC_FSYS, 16, 4),
+ MUX(none, "mout_mipihsi", aclk_p4412, SRC_FSYS, 24, 1),
+ MUX(none, "mout_uart0", group1_p4x12, SRC_PERIL0, 0, 4),
+ MUX(none, "mout_uart1", group1_p4x12, SRC_PERIL0, 4, 4),
+ MUX(none, "mout_uart2", group1_p4x12, SRC_PERIL0, 8, 4),
+ MUX(none, "mout_uart3", group1_p4x12, SRC_PERIL0, 12, 4),
+ MUX(none, "mout_uart4", group1_p4x12, SRC_PERIL0, 16, 4),
+ MUX(none, "mout_audio1", mout_audio1_p4x12, SRC_PERIL1, 0, 4),
+ MUX(none, "mout_audio2", mout_audio2_p4x12, SRC_PERIL1, 4, 4),
+ MUX(none, "mout_spi0", group1_p4x12, SRC_PERIL1, 16, 4),
+ MUX(none, "mout_spi1", group1_p4x12, SRC_PERIL1, 20, 4),
+ MUX(none, "mout_spi2", group1_p4x12, SRC_PERIL1, 24, 4),
+ MUX(none, "mout_pwm_isp", group1_p4x12, E4X12_SRC_ISP, 0, 4),
+ MUX(none, "mout_spi0_isp", group1_p4x12, E4X12_SRC_ISP, 4, 4),
+ MUX(none, "mout_spi1_isp", group1_p4x12, E4X12_SRC_ISP, 8, 4),
+ MUX(none, "mout_uart_isp", group1_p4x12, E4X12_SRC_ISP, 12, 4),
+};
+
+/* list of divider clocks supported in all exynos4 soc's */
+struct samsung_div_clock exynos4_div_clks[] __initdata = {
+ DIV(none, "div_core", "mout_core", DIV_CPU0, 0, 3),
+ DIV(none, "div_core2", "div_core", DIV_CPU0, 28, 3),
+ DIV(none, "div_fimc0", "mout_fimc0", DIV_CAM, 0, 4),
+ DIV(none, "div_fimc1", "mout_fimc1", DIV_CAM, 4, 4),
+ DIV(none, "div_fimc2", "mout_fimc2", DIV_CAM, 8, 4),
+ DIV(none, "div_fimc3", "mout_fimc3", DIV_CAM, 12, 4),
+ DIV(none, "div_cam0", "mout_cam0", DIV_CAM, 16, 4),
+ DIV(none, "div_cam1", "mout_cam1", DIV_CAM, 20, 4),
+ DIV(none, "div_csis0", "mout_csis0", DIV_CAM, 24, 4),
+ DIV(none, "div_csis1", "mout_csis1", DIV_CAM, 28, 4),
+ DIV(sclk_mfc, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4),
+ DIV_F(none, "div_g3d", "mout_g3d", DIV_G3D, 0, 4,
+ CLK_SET_RATE_PARENT, 0),
+ DIV(none, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4),
+ DIV(none, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4),
+ DIV(none, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4),
+ DIV(sclk_pcm0, "sclk_pcm0", "sclk_audio0", DIV_MAUDIO, 4, 8),
+ DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
+ DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
+ DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4),
+ DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4),
+ DIV(sclk_pixel, "sclk_pixel", "sclk_vpll", DIV_TV, 0, 4),
+ DIV(aclk100, "aclk100", "mout_aclk100", DIV_TOP, 4, 4),
+ DIV(aclk160, "aclk160", "mout_aclk160", DIV_TOP, 8, 3),
+ DIV(aclk133, "aclk133", "mout_aclk133", DIV_TOP, 12, 3),
+ DIV(none, "div_onenand", "mout_onenand1", DIV_TOP, 16, 3),
+ DIV(sclk_slimbus, "sclk_slimbus", "sclk_epll", DIV_PERIL3, 4, 4),
+ DIV(sclk_pcm1, "sclk_pcm1", "sclk_audio1", DIV_PERIL4, 4, 8),
+ DIV(sclk_pcm2, "sclk_pcm2", "sclk_audio2", DIV_PERIL4, 20, 8),
+ DIV(sclk_i2s1, "sclk_i2s1", "sclk_audio1", DIV_PERIL5, 0, 6),
+ DIV(sclk_i2s2, "sclk_i2s2", "sclk_audio2", DIV_PERIL5, 8, 6),
+ DIV(none, "div_mmc4", "mout_mmc4", DIV_FSYS3, 0, 4),
+ DIV(none, "div_mmc_pre4", "div_mmc4", DIV_FSYS3, 8, 8),
+ DIV(none, "div_uart0", "mout_uart0", DIV_PERIL0, 0, 4),
+ DIV(none, "div_uart1", "mout_uart1", DIV_PERIL0, 4, 4),
+ DIV(none, "div_uart2", "mout_uart2", DIV_PERIL0, 8, 4),
+ DIV(none, "div_uart3", "mout_uart3", DIV_PERIL0, 12, 4),
+ DIV(none, "div_uart4", "mout_uart4", DIV_PERIL0, 16, 4),
+ DIV(none, "div_spi0", "mout_spi0", DIV_PERIL1, 0, 4),
+ DIV(none, "div_spi_pre0", "div_spi0", DIV_PERIL1, 8, 8),
+ DIV(none, "div_spi1", "mout_spi1", DIV_PERIL1, 16, 4),
+ DIV(none, "div_spi_pre1", "div_spi1", DIV_PERIL1, 24, 8),
+ DIV(none, "div_spi2", "mout_spi2", DIV_PERIL2, 0, 4),
+ DIV(none, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8),
+ DIV(none, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4),
+ DIV(none, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4),
+ DIV_A(arm_clk, "arm_clk", "div_core2", DIV_CPU0, 28, 3, "arm_clk"),
+ DIV_A(sclk_apll, "sclk_apll", "mout_apll",
+ DIV_CPU0, 24, 3, "sclk_apll"),
+ DIV_F(none, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4,
+ CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre0", "div_mmc0", DIV_FSYS1, 8, 8,
+ CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre1", "div_mmc1", DIV_FSYS1, 24, 8,
+ CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre2", "div_mmc2", DIV_FSYS2, 8, 8,
+ CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre3", "div_mmc3", DIV_FSYS2, 24, 8,
+ CLK_SET_RATE_PARENT, 0),
+};
+
+/* list of divider clocks supported in exynos4210 soc */
+struct samsung_div_clock exynos4210_div_clks[] __initdata = {
+ DIV(aclk200, "aclk200", "mout_aclk200", DIV_TOP, 0, 3),
+ DIV(none, "div_g2d", "mout_g2d", DIV_IMAGE, 0, 4),
+ DIV(none, "div_fimd1", "mout_fimd1", E4210_DIV_LCD1, 0, 4),
+ DIV(none, "div_mipi1", "mout_mipi1", E4210_DIV_LCD1, 16, 4),
+ DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4),
+ DIV_F(none, "div_mipi_pre1", "div_mipi1", E4210_DIV_LCD1, 20, 4,
+ CLK_SET_RATE_PARENT, 0),
+};
+
+/* list of divider clocks supported in exynos4x12 soc */
+struct samsung_div_clock exynos4x12_div_clks[] __initdata = {
+ DIV(none, "div_mdnie0", "mout_mdnie0", DIV_LCD0, 4, 4),
+ DIV(none, "div_mdnie_pwm0", "mout_mdnie_pwm0", DIV_LCD0, 8, 4),
+ DIV(none, "div_mdnie_pwm_pre0", "div_mdnie_pwm0", DIV_LCD0, 12, 4),
+ DIV(none, "div_mipihsi", "mout_mipihsi", DIV_FSYS0, 20, 4),
+ DIV(none, "div_jpeg", "mout_jpeg", E4X12_DIV_CAM1, 0, 4),
+ DIV(div_aclk200, "div_aclk200", "mout_aclk200", DIV_TOP, 0, 3),
+ DIV(none, "div_aclk266_gps", "mout_aclk266_gps", DIV_TOP, 20, 3),
+ DIV(div_aclk400_mcuisp, "div_aclk400_mcuisp", "mout_aclk400_mcuisp",
+ DIV_TOP, 24, 3),
+ DIV(none, "div_pwm_isp", "mout_pwm_isp", E4X12_DIV_ISP, 0, 4),
+ DIV(none, "div_spi0_isp", "mout_spi0_isp", E4X12_DIV_ISP, 4, 4),
+ DIV(none, "div_spi0_isp_pre", "div_spi0_isp", E4X12_DIV_ISP, 8, 8),
+ DIV(none, "div_spi1_isp", "mout_spi1_isp", E4X12_DIV_ISP, 16, 4),
+ DIV(none, "div_spi1_isp_pre", "div_spi1_isp", E4X12_DIV_ISP, 20, 8),
+ DIV(none, "div_uart_isp", "mout_uart_isp", E4X12_DIV_ISP, 28, 4),
+ DIV(div_isp0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3),
+ DIV(div_isp1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3),
+ DIV(none, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3),
+ DIV(div_mcuisp0, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, 4, 3),
+ DIV(div_mcuisp1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3),
+};
+
+/* list of gate clocks supported in all exynos4 soc's */
+struct samsung_gate_clock exynos4_gate_clks[] __initdata = {
+ /*
+ * After all Exynos4 based platforms are migrated to use device tree,
+ * the device name and clock alias names specified below for some
+ * of the clocks can be removed.
+ */
+ GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0),
+ GATE(sclk_spdif, "sclk_spdif", "mout_spdif", SRC_MASK_PERIL1, 8, 0, 0),
+ GATE(jpeg, "jpeg", "aclk160", GATE_IP_CAM, 6, 0, 0),
+ GATE(mie0, "mie0", "aclk160", GATE_IP_LCD0, 1, 0, 0),
+ GATE(dsim0, "dsim0", "aclk160", GATE_IP_LCD0, 3, 0, 0),
+ GATE(fimd1, "fimd1", "aclk160", E4210_GATE_IP_LCD1, 0, 0, 0),
+ GATE(mie1, "mie1", "aclk160", E4210_GATE_IP_LCD1, 1, 0, 0),
+ GATE(dsim1, "dsim1", "aclk160", E4210_GATE_IP_LCD1, 3, 0, 0),
+ GATE(smmu_fimd1, "smmu_fimd1", "aclk160", E4210_GATE_IP_LCD1, 4, 0, 0),
+ GATE(tsi, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0),
+ GATE(sromc, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0),
+ GATE(sclk_g3d, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0,
+ CLK_SET_RATE_PARENT, 0),
+ GATE(usb_device, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0),
+ GATE(onenand, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0),
+ GATE(nfcon, "nfcon", "aclk133", GATE_IP_FSYS, 16, 0, 0),
+ GATE(gps, "gps", "aclk133", GATE_IP_GPS, 0, 0, 0),
+ GATE(smmu_gps, "smmu_gps", "aclk133", GATE_IP_GPS, 1, 0, 0),
+ GATE(slimbus, "slimbus", "aclk100", GATE_IP_PERIL, 25, 0, 0),
+ GATE(sclk_cam0, "sclk_cam0", "div_cam0", GATE_SCLK_CAM, 4,
+ CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_cam1, "sclk_cam1", "div_cam1", GATE_SCLK_CAM, 5,
+ CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mipi0, "sclk_mipi0", "div_mipi_pre0",
+ SRC_MASK_LCD0, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_audio0, "sclk_audio0", "div_audio0", SRC_MASK_MAUDIO, 0,
+ CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_audio1, "sclk_audio1", "div_audio1", SRC_MASK_PERIL1, 0,
+ CLK_SET_RATE_PARENT, 0),
+ GATE_D(vp, "s5p-mixer", "vp", "aclk160", GATE_IP_TV, 0, 0, 0),
+ GATE_D(mixer, "s5p-mixer", "mixer", "aclk160", GATE_IP_TV, 1, 0, 0),
+ GATE_D(hdmi, "exynos4-hdmi", "hdmi", "aclk160", GATE_IP_TV, 3, 0, 0),
+ GATE_A(pwm, "pwm", "aclk100", GATE_IP_PERIL, 24, 0, 0, "timers"),
+ GATE_A(sdmmc4, "sdmmc4", "aclk133", GATE_IP_FSYS, 9, 0, 0, "biu"),
+ GATE_A(usb_host, "usb_host", "aclk133",
+ GATE_IP_FSYS, 12, 0, 0, "usbhost"),
+ GATE_DA(sclk_fimc0, "exynos4-fimc.0", "sclk_fimc0", "div_fimc0",
+ SRC_MASK_CAM, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimc"),
+ GATE_DA(sclk_fimc1, "exynos4-fimc.1", "sclk_fimc1", "div_fimc1",
+ SRC_MASK_CAM, 4, CLK_SET_RATE_PARENT, 0, "sclk_fimc"),
+ GATE_DA(sclk_fimc2, "exynos4-fimc.2", "sclk_fimc2", "div_fimc2",
+ SRC_MASK_CAM, 8, CLK_SET_RATE_PARENT, 0, "sclk_fimc"),
+ GATE_DA(sclk_fimc3, "exynos4-fimc.3", "sclk_fimc3", "div_fimc3",
+ SRC_MASK_CAM, 12, CLK_SET_RATE_PARENT, 0, "sclk_fimc"),
+ GATE_DA(sclk_csis0, "s5p-mipi-csis.0", "sclk_csis0", "div_csis0",
+ SRC_MASK_CAM, 24, CLK_SET_RATE_PARENT, 0, "sclk_csis"),
+ GATE_DA(sclk_csis1, "s5p-mipi-csis.1", "sclk_csis1", "div_csis1",
+ SRC_MASK_CAM, 28, CLK_SET_RATE_PARENT, 0, "sclk_csis"),
+ GATE_DA(sclk_fimd0, "exynos4-fb.0", "sclk_fimd0", "div_fimd0",
+ SRC_MASK_LCD0, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"),
+ GATE_DA(sclk_mmc0, "exynos4-sdhci.0", "sclk_mmc0", "div_mmc_pre0",
+ SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0,
+ "mmc_busclk.2"),
+ GATE_DA(sclk_mmc1, "exynos4-sdhci.1", "sclk_mmc1", "div_mmc_pre1",
+ SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0,
+ "mmc_busclk.2"),
+ GATE_DA(sclk_mmc2, "exynos4-sdhci.2", "sclk_mmc2", "div_mmc_pre2",
+ SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0,
+ "mmc_busclk.2"),
+ GATE_DA(sclk_mmc3, "exynos4-sdhci.3", "sclk_mmc3", "div_mmc_pre3",
+ SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0,
+ "mmc_busclk.2"),
+ GATE_DA(sclk_mmc4, NULL, "sclk_mmc4", "div_mmc_pre4",
+ SRC_MASK_FSYS, 16, CLK_SET_RATE_PARENT, 0, "ciu"),
+ GATE_DA(sclk_uart0, "exynos4210-uart.0", "uclk0", "div_uart0",
+ SRC_MASK_PERIL0, 0, CLK_SET_RATE_PARENT,
+ 0, "clk_uart_baud0"),
+ GATE_DA(sclk_uart1, "exynos4210-uart.1", "uclk1", "div_uart1",
+ SRC_MASK_PERIL0, 4, CLK_SET_RATE_PARENT,
+ 0, "clk_uart_baud0"),
+ GATE_DA(sclk_uart2, "exynos4210-uart.2", "uclk2", "div_uart2",
+ SRC_MASK_PERIL0, 8, CLK_SET_RATE_PARENT,
+ 0, "clk_uart_baud0"),
+ GATE_DA(sclk_uart3, "exynos4210-uart.3", "uclk3", "div_uart3",
+ SRC_MASK_PERIL0, 12, CLK_SET_RATE_PARENT,
+ 0, "clk_uart_baud0"),
+ GATE_DA(sclk_uart4, "exynos4210-uart.4", "uclk4", "div_uart4",
+ SRC_MASK_PERIL0, 16, CLK_SET_RATE_PARENT,
+ 0, "clk_uart_baud0"),
+ GATE(sclk_audio2, "sclk_audio2", "div_audio2", SRC_MASK_PERIL1, 4,
+ CLK_SET_RATE_PARENT, 0),
+ GATE_DA(sclk_spi0, "exynos4210-spi.0", "sclk_spi0", "div_spi_pre0",
+ SRC_MASK_PERIL1, 16, CLK_SET_RATE_PARENT,
+ 0, "spi_busclk0"),
+ GATE_DA(sclk_spi1, "exynos4210-spi.1", "sclk_spi1", "div_spi_pre1",
+ SRC_MASK_PERIL1, 20, CLK_SET_RATE_PARENT,
+ 0, "spi_busclk0"),
+ GATE_DA(sclk_spi2, "exynos4210-spi.2", "sclk_spi2", "div_spi_pre2",
+ SRC_MASK_PERIL1, 24, CLK_SET_RATE_PARENT,
+ 0, "spi_busclk0"),
+ GATE_DA(fimc0, "exynos4-fimc.0", "fimc0", "aclk160",
+ GATE_IP_CAM, 0, 0, 0, "fimc"),
+ GATE_DA(fimc1, "exynos4-fimc.1", "fimc1", "aclk160",
+ GATE_IP_CAM, 1, 0, 0, "fimc"),
+ GATE_DA(fimc2, "exynos4-fimc.2", "fimc2", "aclk160",
+ GATE_IP_CAM, 2, 0, 0, "fimc"),
+ GATE_DA(fimc3, "exynos4-fimc.3", "fimc3", "aclk160",
+ GATE_IP_CAM, 3, 0, 0, "fimc"),
+ GATE_DA(csis0, "s5p-mipi-csis.0", "csis0", "aclk160",
+ GATE_IP_CAM, 4, 0, 0, "fimc"),
+ GATE_DA(csis1, "s5p-mipi-csis.1", "csis1", "aclk160",
+ GATE_IP_CAM, 5, 0, 0, "fimc"),
+ GATE_DA(smmu_fimc0, "exynos-sysmmu.5", "smmu_fimc0", "aclk160",
+ GATE_IP_CAM, 7, 0, 0, "sysmmu"),
+ GATE_DA(smmu_fimc1, "exynos-sysmmu.6", "smmu_fimc1", "aclk160",
+ GATE_IP_CAM, 8, 0, 0, "sysmmu"),
+ GATE_DA(smmu_fimc2, "exynos-sysmmu.7", "smmu_fimc2", "aclk160",
+ GATE_IP_CAM, 9, 0, 0, "sysmmu"),
+ GATE_DA(smmu_fimc3, "exynos-sysmmu.8", "smmu_fimc3", "aclk160",
+ GATE_IP_CAM, 10, 0, 0, "sysmmu"),
+ GATE_DA(smmu_jpeg, "exynos-sysmmu.3", "smmu_jpeg", "aclk160",
+ GATE_IP_CAM, 11, 0, 0, "sysmmu"),
+ GATE(pixelasyncm0, "pxl_async0", "aclk160", GATE_IP_CAM, 17, 0, 0),
+ GATE(pixelasyncm1, "pxl_async1", "aclk160", GATE_IP_CAM, 18, 0, 0),
+ GATE_DA(smmu_tv, "exynos-sysmmu.2", "smmu_tv", "aclk160",
+ GATE_IP_TV, 4, 0, 0, "sysmmu"),
+ GATE_DA(mfc, "s5p-mfc", "mfc", "aclk100", GATE_IP_MFC, 0, 0, 0, "mfc"),
+ GATE_DA(smmu_mfcl, "exynos-sysmmu.0", "smmu_mfcl", "aclk100",
+ GATE_IP_MFC, 1, 0, 0, "sysmmu"),
+ GATE_DA(smmu_mfcr, "exynos-sysmmu.1", "smmu_mfcr", "aclk100",
+ GATE_IP_MFC, 2, 0, 0, "sysmmu"),
+ GATE_DA(fimd0, "exynos4-fb.0", "fimd0", "aclk160",
+ GATE_IP_LCD0, 0, 0, 0, "fimd"),
+ GATE_DA(smmu_fimd0, "exynos-sysmmu.10", "smmu_fimd0", "aclk160",
+ GATE_IP_LCD0, 4, 0, 0, "sysmmu"),
+ GATE_DA(pdma0, "dma-pl330.0", "pdma0", "aclk133",
+ GATE_IP_FSYS, 0, 0, 0, "dma"),
+ GATE_DA(pdma1, "dma-pl330.1", "pdma1", "aclk133",
+ GATE_IP_FSYS, 1, 0, 0, "dma"),
+ GATE_DA(sdmmc0, "exynos4-sdhci.0", "sdmmc0", "aclk133",
+ GATE_IP_FSYS, 5, 0, 0, "hsmmc"),
+ GATE_DA(sdmmc1, "exynos4-sdhci.1", "sdmmc1", "aclk133",
+ GATE_IP_FSYS, 6, 0, 0, "hsmmc"),
+ GATE_DA(sdmmc2, "exynos4-sdhci.2", "sdmmc2", "aclk133",
+ GATE_IP_FSYS, 7, 0, 0, "hsmmc"),
+ GATE_DA(sdmmc3, "exynos4-sdhci.3", "sdmmc3", "aclk133",
+ GATE_IP_FSYS, 8, 0, 0, "hsmmc"),
+ GATE_DA(uart0, "exynos4210-uart.0", "uart0", "aclk100",
+ GATE_IP_PERIL, 0, 0, 0, "uart"),
+ GATE_DA(uart1, "exynos4210-uart.1", "uart1", "aclk100",
+ GATE_IP_PERIL, 1, 0, 0, "uart"),
+ GATE_DA(uart2, "exynos4210-uart.2", "uart2", "aclk100",
+ GATE_IP_PERIL, 2, 0, 0, "uart"),
+ GATE_DA(uart3, "exynos4210-uart.3", "uart3", "aclk100",
+ GATE_IP_PERIL, 3, 0, 0, "uart"),
+ GATE_DA(uart4, "exynos4210-uart.4", "uart4", "aclk100",
+ GATE_IP_PERIL, 4, 0, 0, "uart"),
+ GATE_DA(i2c0, "s3c2440-i2c.0", "i2c0", "aclk100",
+ GATE_IP_PERIL, 6, 0, 0, "i2c"),
+ GATE_DA(i2c1, "s3c2440-i2c.1", "i2c1", "aclk100",
+ GATE_IP_PERIL, 7, 0, 0, "i2c"),
+ GATE_DA(i2c2, "s3c2440-i2c.2", "i2c2", "aclk100",
+ GATE_IP_PERIL, 8, 0, 0, "i2c"),
+ GATE_DA(i2c3, "s3c2440-i2c.3", "i2c3", "aclk100",
+ GATE_IP_PERIL, 9, 0, 0, "i2c"),
+ GATE_DA(i2c4, "s3c2440-i2c.4", "i2c4", "aclk100",
+ GATE_IP_PERIL, 10, 0, 0, "i2c"),
+ GATE_DA(i2c5, "s3c2440-i2c.5", "i2c5", "aclk100",
+ GATE_IP_PERIL, 11, 0, 0, "i2c"),
+ GATE_DA(i2c6, "s3c2440-i2c.6", "i2c6", "aclk100",
+ GATE_IP_PERIL, 12, 0, 0, "i2c"),
+ GATE_DA(i2c7, "s3c2440-i2c.7", "i2c7", "aclk100",
+ GATE_IP_PERIL, 13, 0, 0, "i2c"),
+ GATE_DA(i2c_hdmi, "s3c2440-hdmiphy-i2c", "i2c-hdmi", "aclk100",
+ GATE_IP_PERIL, 14, 0, 0, "i2c"),
+ GATE_DA(spi0, "exynos4210-spi.0", "spi0", "aclk100",
+ GATE_IP_PERIL, 16, 0, 0, "spi"),
+ GATE_DA(spi1, "exynos4210-spi.1", "spi1", "aclk100",
+ GATE_IP_PERIL, 17, 0, 0, "spi"),
+ GATE_DA(spi2, "exynos4210-spi.2", "spi2", "aclk100",
+ GATE_IP_PERIL, 18, 0, 0, "spi"),
+ GATE_DA(i2s1, "samsung-i2s.1", "i2s1", "aclk100",
+ GATE_IP_PERIL, 20, 0, 0, "iis"),
+ GATE_DA(i2s2, "samsung-i2s.2", "i2s2", "aclk100",
+ GATE_IP_PERIL, 21, 0, 0, "iis"),
+ GATE_DA(pcm1, "samsung-pcm.1", "pcm1", "aclk100",
+ GATE_IP_PERIL, 22, 0, 0, "pcm"),
+ GATE_DA(pcm2, "samsung-pcm.2", "pcm2", "aclk100",
+ GATE_IP_PERIL, 23, 0, 0, "pcm"),
+ GATE_DA(spdif, "samsung-spdif", "spdif", "aclk100",
+ GATE_IP_PERIL, 26, 0, 0, "spdif"),
+ GATE_DA(ac97, "samsung-ac97", "ac97", "aclk100",
+ GATE_IP_PERIL, 27, 0, 0, "ac97"),
+};
+
+/* list of gate clocks supported in exynos4210 soc */
+struct samsung_gate_clock exynos4210_gate_clks[] __initdata = {
+ GATE(tvenc, "tvenc", "aclk160", GATE_IP_TV, 2, 0, 0),
+ GATE(g2d, "g2d", "aclk200", E4210_GATE_IP_IMAGE, 0, 0, 0),
+ GATE(rotator, "rotator", "aclk200", E4210_GATE_IP_IMAGE, 1, 0, 0),
+ GATE(mdma, "mdma", "aclk200", E4210_GATE_IP_IMAGE, 2, 0, 0),
+ GATE(smmu_g2d, "smmu_g2d", "aclk200", E4210_GATE_IP_IMAGE, 3, 0, 0),
+ GATE(smmu_mdma, "smmu_mdma", "aclk200", E4210_GATE_IP_IMAGE, 5, 0, 0),
+ GATE(pcie_phy, "pcie_phy", "aclk133", GATE_IP_FSYS, 2, 0, 0),
+ GATE(sata_phy, "sata_phy", "aclk133", GATE_IP_FSYS, 3, 0, 0),
+ GATE(sata, "sata", "aclk133", GATE_IP_FSYS, 10, 0, 0),
+ GATE(pcie, "pcie", "aclk133", GATE_IP_FSYS, 14, 0, 0),
+ GATE(smmu_pcie, "smmu_pcie", "aclk133", GATE_IP_FSYS, 18, 0, 0),
+ GATE(modemif, "modemif", "aclk100", GATE_IP_PERIL, 28, 0, 0),
+ GATE(chipid, "chipid", "aclk100", E4210_GATE_IP_PERIR, 0, 0, 0),
+ GATE(sysreg, "sysreg", "aclk100", E4210_GATE_IP_PERIR, 0,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(hdmi_cec, "hdmi_cec", "aclk100", E4210_GATE_IP_PERIR, 11, 0, 0),
+ GATE(smmu_rotator, "smmu_rotator", "aclk200",
+ E4210_GATE_IP_IMAGE, 4, 0, 0),
+ GATE(sclk_mipi1, "sclk_mipi1", "div_mipi_pre1",
+ E4210_SRC_MASK_LCD1, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_sata, "sclk_sata", "div_sata",
+ SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0),
+ GATE(sclk_dac, "sclk_dac", "mout_dac", SRC_MASK_TV, 8, 0, 0),
+ GATE_A(tsadc, "tsadc", "aclk100", GATE_IP_PERIL, 15, 0, 0, "adc"),
+ GATE_A(mct, "mct", "aclk100", E4210_GATE_IP_PERIR, 13, 0, 0, "mct"),
+ GATE_A(wdt, "watchdog", "aclk100", E4210_GATE_IP_PERIR, 14, 0, 0, "watchdog"),
+ GATE_A(rtc, "rtc", "aclk100", E4210_GATE_IP_PERIR, 15, 0, 0, "rtc"),
+ GATE_A(keyif, "keyif", "aclk100", E4210_GATE_IP_PERIR, 16, 0, 0, "keypad"),
+ GATE_DA(sclk_fimd1, "exynos4-fb.1", "sclk_fimd1", "div_fimd1",
+ E4210_SRC_MASK_LCD1, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"),
+};
+
+/* list of gate clocks supported in exynos4x12 soc */
+struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = {
+ GATE(audss, "audss", "sclk_epll", E4X12_GATE_IP_MAUDIO, 0, 0, 0),
+ GATE(mdnie0, "mdnie0", "aclk160", GATE_IP_LCD0, 2, 0, 0),
+ GATE(rotator, "rotator", "aclk200", E4X12_GATE_IP_IMAGE, 1, 0, 0),
+ GATE(mdma2, "mdma2", "aclk200", E4X12_GATE_IP_IMAGE, 2, 0, 0),
+ GATE(smmu_mdma, "smmu_mdma", "aclk200", E4X12_GATE_IP_IMAGE, 5, 0, 0),
+ GATE(mipi_hsi, "mipi_hsi", "aclk133", GATE_IP_FSYS, 10, 0, 0),
+ GATE(chipid, "chipid", "aclk100", E4X12_GATE_IP_PERIR, 0, 0, 0),
+ GATE(sysreg, "sysreg", "aclk100", E4X12_GATE_IP_PERIR, 1,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(hdmi_cec, "hdmi_cec", "aclk100", E4X12_GATE_IP_PERIR, 11, 0, 0),
+ GATE(sclk_mdnie0, "sclk_mdnie0", "div_mdnie0",
+ SRC_MASK_LCD0, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mdnie_pwm0, "sclk_mdnie_pwm0", "div_mdnie_pwm_pre0",
+ SRC_MASK_LCD0, 8, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mipihsi, "sclk_mipihsi", "div_mipihsi",
+ SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
+ GATE(smmu_rotator, "smmu_rotator", "aclk200",
+ E4X12_GATE_IP_IMAGE, 4, 0, 0),
+ GATE_A(mct, "mct", "aclk100", E4X12_GATE_IP_PERIR, 13, 0, 0, "mct"),
+ GATE_A(rtc, "rtc", "aclk100", E4X12_GATE_IP_PERIR, 15, 0, 0, "rtc"),
+ GATE_A(keyif, "keyif", "aclk100",
+ E4X12_GATE_IP_PERIR, 16, 0, 0, "keypad"),
+ GATE(sclk_pwm_isp, "sclk_pwm_isp", "div_pwm_isp",
+ E4X12_SRC_MASK_ISP, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_spi0_isp, "sclk_spi0_isp", "div_spi0_isp_pre",
+ E4X12_SRC_MASK_ISP, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_spi1_isp, "sclk_spi1_isp", "div_spi1_isp_pre",
+ E4X12_SRC_MASK_ISP, 8, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_uart_isp, "sclk_uart_isp", "div_uart_isp",
+ E4X12_SRC_MASK_ISP, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(pwm_isp_sclk, "pwm_isp_sclk", "sclk_pwm_isp",
+ E4X12_GATE_IP_ISP, 0, 0, 0),
+ GATE(spi0_isp_sclk, "spi0_isp_sclk", "sclk_spi0_isp",
+ E4X12_GATE_IP_ISP, 1, 0, 0),
+ GATE(spi1_isp_sclk, "spi1_isp_sclk", "sclk_spi1_isp",
+ E4X12_GATE_IP_ISP, 2, 0, 0),
+ GATE(uart_isp_sclk, "uart_isp_sclk", "sclk_uart_isp",
+ E4X12_GATE_IP_ISP, 3, 0, 0),
+ GATE_A(wdt, "watchdog", "aclk100",
+ E4X12_GATE_IP_PERIR, 14, 0, 0, "watchdog"),
+ GATE_DA(pcm0, "samsung-pcm.0", "pcm0", "aclk100",
+ E4X12_GATE_IP_MAUDIO, 2, 0, 0, "pcm"),
+ GATE_DA(i2s0, "samsung-i2s.0", "i2s0", "aclk100",
+ E4X12_GATE_IP_MAUDIO, 3, 0, 0, "iis"),
+ GATE(fimc_isp, "isp", "aclk200", E4X12_GATE_ISP0, 0,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(fimc_drc, "drc", "aclk200", E4X12_GATE_ISP0, 1,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(fimc_fd, "fd", "aclk200", E4X12_GATE_ISP0, 2,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(fimc_lite0, "lite0", "aclk200", E4X12_GATE_ISP0, 3,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(fimc_lite1, "lite1", "aclk200", E4X12_GATE_ISP0, 4,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(mcuisp, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(gicisp, "gicisp", "aclk200", E4X12_GATE_ISP0, 7,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(smmu_isp, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(smmu_drc, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(smmu_fd, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(smmu_lite0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(smmu_lite1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(ppmuispmx, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(ppmuispx, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(mcuctl_isp, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(mpwm_isp, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(i2c0_isp, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(i2c1_isp, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(mtcadc_isp, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(pwm_isp, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(wdt_isp, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(uart_isp, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(asyncaxim, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(smmu_ispcx, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(spi0_isp, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12,
+ CLK_IGNORE_UNUSED, 0),
+ GATE(spi1_isp, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13,
+ CLK_IGNORE_UNUSED, 0),
+};
+
+/*
+ * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit
+ * resides in chipid register space, outside of the clock controller memory
+ * mapped space. So to determine the parent of fin_pll clock, the chipid
+ * controller is first remapped and the value of XOM[0] bit is read to
+ * determine the parent clock.
+ */
+static unsigned long exynos4_get_xom(void)
+{
+ unsigned long xom = 0;
+ void __iomem *chipid_base;
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid");
+ if (np) {
+ chipid_base = of_iomap(np, 0);
+
+ if (chipid_base)
+ xom = readl(chipid_base + 8);
+
+ iounmap(chipid_base);
+ }
+
+ return xom;
+}
+
+static void __init exynos4_clk_register_finpll(unsigned long xom)
+{
+ struct samsung_fixed_rate_clock fclk;
+ struct clk *clk;
+ unsigned long finpll_f = 24000000;
+ char *parent_name;
+
+ parent_name = xom & 1 ? "xusbxti" : "xxti";
+ clk = clk_get(NULL, parent_name);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to lookup parent clock %s, assuming "
+ "fin_pll clock frequency is 24MHz\n", __func__,
+ parent_name);
+ } else {
+ finpll_f = clk_get_rate(clk);
+ }
+
+ fclk.id = fin_pll;
+ fclk.name = "fin_pll";
+ fclk.parent_name = NULL;
+ fclk.flags = CLK_IS_ROOT;
+ fclk.fixed_rate = finpll_f;
+ samsung_clk_register_fixed_rate(&fclk, 1);
+
+}
+
+/*
+ * This function allows non-dt platforms to specify the clock speed of the
+ * xxti and xusbxti clocks. These clocks are then registered with the specified
+ * clock speed.
+ */
+void __init exynos4_clk_register_fixed_ext(unsigned long xxti_f,
+ unsigned long xusbxti_f)
+{
+ exynos4_fixed_rate_ext_clks[0].fixed_rate = xxti_f;
+ exynos4_fixed_rate_ext_clks[1].fixed_rate = xusbxti_f;
+ samsung_clk_register_fixed_rate(exynos4_fixed_rate_ext_clks,
+ ARRAY_SIZE(exynos4_fixed_rate_ext_clks));
+}
+
+static __initdata struct of_device_id ext_clk_match[] = {
+ { .compatible = "samsung,clock-xxti", .data = (void *)0, },
+ { .compatible = "samsung,clock-xusbxti", .data = (void *)1, },
+ {},
+};
+
+/* register exynos4 clocks */
+void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom)
+{
+ struct clk *apll, *mpll, *epll, *vpll;
+
+ if (np) {
+ reg_base = of_iomap(np, 0);
+ if (!reg_base)
+ panic("%s: failed to map registers\n", __func__);
+ }
+
+ if (exynos4_soc == EXYNOS4210)
+ samsung_clk_init(np, reg_base, nr_clks,
+ exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
+ exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save));
+ else
+ samsung_clk_init(np, reg_base, nr_clks,
+ exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
+ exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save));
+
+ if (np)
+ samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks,
+ ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
+ ext_clk_match);
+
+ exynos4_clk_register_finpll(xom);
+
+ if (exynos4_soc == EXYNOS4210) {
+ apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll",
+ reg_base + APLL_CON0, pll_4508);
+ mpll = samsung_clk_register_pll45xx("fout_mpll", "fin_pll",
+ reg_base + E4210_MPLL_CON0, pll_4508);
+ epll = samsung_clk_register_pll46xx("fout_epll", "fin_pll",
+ reg_base + EPLL_CON0, pll_4600);
+ vpll = samsung_clk_register_pll46xx("fout_vpll", "mout_vpllsrc",
+ reg_base + VPLL_CON0, pll_4650c);
+ } else {
+ apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll",
+ reg_base + APLL_CON0);
+ mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll",
+ reg_base + E4X12_MPLL_CON0);
+ epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll",
+ reg_base + EPLL_CON0);
+ vpll = samsung_clk_register_pll36xx("fout_vpll", "fin_pll",
+ reg_base + VPLL_CON0);
+ }
+
+ samsung_clk_add_lookup(apll, fout_apll);
+ samsung_clk_add_lookup(mpll, fout_mpll);
+ samsung_clk_add_lookup(epll, fout_epll);
+ samsung_clk_add_lookup(vpll, fout_vpll);
+
+ samsung_clk_register_fixed_rate(exynos4_fixed_rate_clks,
+ ARRAY_SIZE(exynos4_fixed_rate_clks));
+ samsung_clk_register_mux(exynos4_mux_clks,
+ ARRAY_SIZE(exynos4_mux_clks));
+ samsung_clk_register_div(exynos4_div_clks,
+ ARRAY_SIZE(exynos4_div_clks));
+ samsung_clk_register_gate(exynos4_gate_clks,
+ ARRAY_SIZE(exynos4_gate_clks));
+
+ if (exynos4_soc == EXYNOS4210) {
+ samsung_clk_register_fixed_rate(exynos4210_fixed_rate_clks,
+ ARRAY_SIZE(exynos4210_fixed_rate_clks));
+ samsung_clk_register_mux(exynos4210_mux_clks,
+ ARRAY_SIZE(exynos4210_mux_clks));
+ samsung_clk_register_div(exynos4210_div_clks,
+ ARRAY_SIZE(exynos4210_div_clks));
+ samsung_clk_register_gate(exynos4210_gate_clks,
+ ARRAY_SIZE(exynos4210_gate_clks));
+ } else {
+ samsung_clk_register_mux(exynos4x12_mux_clks,
+ ARRAY_SIZE(exynos4x12_mux_clks));
+ samsung_clk_register_div(exynos4x12_div_clks,
+ ARRAY_SIZE(exynos4x12_div_clks));
+ samsung_clk_register_gate(exynos4x12_gate_clks,
+ ARRAY_SIZE(exynos4x12_gate_clks));
+ }
+
+ pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n"
+ "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n",
+ exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12",
+ _get_rate("sclk_apll"), _get_rate("sclk_mpll"),
+ _get_rate("sclk_epll"), _get_rate("sclk_vpll"),
+ _get_rate("arm_clk"));
+}
+
+
+static void __init exynos4210_clk_init(struct device_node *np)
+{
+ exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
+}
+CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
+
+static void __init exynos4412_clk_init(struct device_node *np)
+{
+ exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
+}
+CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
new file mode 100644
index 000000000..92afc0606
--- /dev/null
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab@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 Exynos5250 SoC.
+*/
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk.h"
+#include "clk-pll.h"
+
+#define SRC_CPU 0x200
+#define DIV_CPU0 0x500
+#define SRC_CORE1 0x4204
+#define SRC_TOP0 0x10210
+#define SRC_TOP2 0x10218
+#define SRC_GSCL 0x10220
+#define SRC_DISP1_0 0x1022c
+#define SRC_MAU 0x10240
+#define SRC_FSYS 0x10244
+#define SRC_GEN 0x10248
+#define SRC_PERIC0 0x10250
+#define SRC_PERIC1 0x10254
+#define SRC_MASK_GSCL 0x10320
+#define SRC_MASK_DISP1_0 0x1032c
+#define SRC_MASK_MAU 0x10334
+#define SRC_MASK_FSYS 0x10340
+#define SRC_MASK_GEN 0x10344
+#define SRC_MASK_PERIC0 0x10350
+#define SRC_MASK_PERIC1 0x10354
+#define DIV_TOP0 0x10510
+#define DIV_TOP1 0x10514
+#define DIV_GSCL 0x10520
+#define DIV_DISP1_0 0x1052c
+#define DIV_GEN 0x1053c
+#define DIV_MAU 0x10544
+#define DIV_FSYS0 0x10548
+#define DIV_FSYS1 0x1054c
+#define DIV_FSYS2 0x10550
+#define DIV_PERIC0 0x10558
+#define DIV_PERIC1 0x1055c
+#define DIV_PERIC2 0x10560
+#define DIV_PERIC3 0x10564
+#define DIV_PERIC4 0x10568
+#define DIV_PERIC5 0x1056c
+#define GATE_IP_GSCL 0x10920
+#define GATE_IP_MFC 0x1092c
+#define GATE_IP_GEN 0x10934
+#define GATE_IP_FSYS 0x10944
+#define GATE_IP_PERIC 0x10950
+#define GATE_IP_PERIS 0x10960
+#define SRC_CDREX 0x20200
+#define PLL_DIV2_SEL 0x20a24
+#define GATE_IP_DISP1 0x10928
+
+/*
+ * Let each supported clock get a unique id. This id is used to lookup the clock
+ * for device tree based platforms. The clocks are categorized into three
+ * sections: core, sclk gate and bus interface gate clocks.
+ *
+ * When adding a new clock to this list, it is advised to choose a clock
+ * category and add it to the end of that category. That is because the the
+ * device tree source file is referring to these ids and any change in the
+ * sequence number of existing clocks will require corresponding change in the
+ * device tree files. This limitation would go away when pre-processor support
+ * for dtc would be available.
+ */
+enum exynos5250_clks {
+ none,
+
+ /* core clocks */
+ fin_pll,
+
+ /* gate for special clocks (sclk) */
+ sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb,
+ sclk_fimd1, sclk_mipi1, sclk_dp, sclk_hdmi, sclk_pixel, sclk_audio0,
+ sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_sata, sclk_usb3,
+ sclk_jpeg, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_pwm,
+ sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2,
+
+ /* gate clocks */
+ gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0,
+ smmu_gscl1, smmu_gscl2, smmu_gscl3, mfc, smmu_mfcl, smmu_mfcr, rotator,
+ jpeg, mdma1, smmu_rotator, smmu_jpeg, smmu_mdma1, pdma0, pdma1, sata,
+ usbotg, mipi_hsi, sdmmc0, sdmmc1, sdmmc2, sdmmc3, sromc, usb2, usb3,
+ sata_phyctrl, sata_phyi2c, uart0, uart1, uart2, uart3, uart4, i2c0,
+ i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, adc, spi0, spi1,
+ spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2,
+ hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1,
+ tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct,
+ wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi,
+
+ nr_clks,
+};
+
+/*
+ * list of controller registers to be saved and restored during a
+ * suspend/resume cycle.
+ */
+static __initdata unsigned long exynos5250_clk_regs[] = {
+ SRC_CPU,
+ DIV_CPU0,
+ SRC_CORE1,
+ SRC_TOP0,
+ SRC_TOP2,
+ SRC_GSCL,
+ SRC_DISP1_0,
+ SRC_MAU,
+ SRC_FSYS,
+ SRC_GEN,
+ SRC_PERIC0,
+ SRC_PERIC1,
+ SRC_MASK_GSCL,
+ SRC_MASK_DISP1_0,
+ SRC_MASK_MAU,
+ SRC_MASK_FSYS,
+ SRC_MASK_GEN,
+ SRC_MASK_PERIC0,
+ SRC_MASK_PERIC1,
+ DIV_TOP0,
+ DIV_TOP1,
+ DIV_GSCL,
+ DIV_DISP1_0,
+ DIV_GEN,
+ DIV_MAU,
+ DIV_FSYS0,
+ DIV_FSYS1,
+ DIV_FSYS2,
+ DIV_PERIC0,
+ DIV_PERIC1,
+ DIV_PERIC2,
+ DIV_PERIC3,
+ DIV_PERIC4,
+ DIV_PERIC5,
+ GATE_IP_GSCL,
+ GATE_IP_MFC,
+ GATE_IP_GEN,
+ GATE_IP_FSYS,
+ GATE_IP_PERIC,
+ GATE_IP_PERIS,
+ SRC_CDREX,
+ PLL_DIV2_SEL,
+ GATE_IP_DISP1,
+};
+
+/* list of all parent clock list */
+PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
+PNAME(mout_cpu_p) = { "mout_apll", "sclk_mpll", };
+PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" };
+PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" };
+PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" };
+PNAME(mout_bpll_p) = { "fin_pll", "mout_bpll_fout" };
+PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" };
+PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" };
+PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" };
+PNAME(mout_epll_p) = { "fin_pll", "fout_epll" };
+PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" };
+PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" };
+PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" };
+PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" };
+PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" };
+PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" };
+PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m",
+ "sclk_dptxphy", "sclk_uhostphy", "sclk_hdmiphy",
+ "sclk_mpll_user", "sclk_epll", "sclk_vpll",
+ "sclk_cpll" };
+PNAME(mout_audio0_p) = { "cdclk0", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
+ "sclk_uhostphy", "sclk_hdmiphy",
+ "sclk_mpll_user", "sclk_epll", "sclk_vpll",
+ "sclk_cpll" };
+PNAME(mout_audio1_p) = { "cdclk1", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
+ "sclk_uhostphy", "sclk_hdmiphy",
+ "sclk_mpll_user", "sclk_epll", "sclk_vpll",
+ "sclk_cpll" };
+PNAME(mout_audio2_p) = { "cdclk2", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
+ "sclk_uhostphy", "sclk_hdmiphy",
+ "sclk_mpll_user", "sclk_epll", "sclk_vpll",
+ "sclk_cpll" };
+PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2",
+ "spdif_extclk" };
+
+/* fixed rate clocks generated outside the soc */
+struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = {
+ FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0),
+};
+
+/* fixed rate clocks generated inside the soc */
+struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = {
+ FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000),
+ FRATE(none, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000),
+ FRATE(none, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000),
+ FRATE(none, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000),
+};
+
+struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = {
+ FFACTOR(none, "fout_mplldiv2", "fout_mpll", 1, 2, 0),
+ FFACTOR(none, "fout_bplldiv2", "fout_bpll", 1, 2, 0),
+};
+
+struct samsung_mux_clock exynos5250_mux_clks[] __initdata = {
+ MUX_A(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, "mout_apll"),
+ MUX_A(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"),
+ MUX(none, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1),
+ MUX_A(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"),
+ MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1),
+ MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1),
+ MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1),
+ MUX(none, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1),
+ MUX(none, "sclk_epll", mout_epll_p, SRC_TOP2, 12, 1),
+ MUX(none, "sclk_cpll", mout_cpll_p, SRC_TOP2, 8, 1),
+ MUX(none, "sclk_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1),
+ MUX(none, "sclk_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1),
+ MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
+ MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
+ MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
+ MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4),
+ MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4),
+ MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4),
+ MUX(none, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4),
+ MUX(none, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4),
+ MUX(none, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4),
+ MUX(none, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4),
+ MUX(none, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4),
+ MUX(none, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1),
+ MUX(none, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4),
+ MUX(none, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4),
+ MUX(none, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4),
+ MUX(none, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4),
+ MUX(none, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4),
+ MUX(none, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1),
+ MUX(none, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1),
+ MUX(none, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4),
+ MUX(none, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4),
+ MUX(none, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4),
+ MUX(none, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4),
+ MUX(none, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4),
+ MUX(none, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4),
+ MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4),
+ MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4),
+ MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2),
+ MUX(none, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4),
+ MUX(none, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4),
+ MUX(none, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4),
+};
+
+struct samsung_div_clock exynos5250_div_clks[] __initdata = {
+ DIV(none, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
+ DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
+ DIV(none, "aclk66_pre", "sclk_mpll_user", DIV_TOP1, 24, 3),
+ DIV(none, "aclk66", "aclk66_pre", DIV_TOP0, 0, 3),
+ DIV(none, "aclk266", "sclk_mpll_user", DIV_TOP0, 16, 3),
+ DIV(none, "aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
+ DIV(none, "aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
+ DIV(none, "aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
+ DIV(none, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4),
+ DIV(none, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4),
+ DIV(none, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4),
+ DIV(none, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4),
+ DIV(none, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4),
+ DIV(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4),
+ DIV(none, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4),
+ DIV(none, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4),
+ DIV(none, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4),
+ DIV(none, "div_audio0", "mout_audio0", DIV_MAU, 0, 4),
+ DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8),
+ DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4),
+ DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4),
+ DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
+ DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
+ DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4),
+ DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4),
+ DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4),
+ DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4),
+ DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4),
+ DIV(none, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4),
+ DIV(none, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4),
+ DIV(none, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4),
+ DIV(none, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4),
+ DIV(none, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4),
+ DIV(none, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4),
+ DIV(none, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8),
+ DIV(none, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4),
+ DIV(none, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8),
+ DIV(none, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6),
+ DIV(none, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6),
+ DIV(sclk_pixel, "div_hdmi_pixel", "sclk_vpll", DIV_DISP1_0, 28, 4),
+ DIV_A(none, "armclk", "div_arm", DIV_CPU0, 28, 3, "armclk"),
+ DIV_F(none, "div_mipi1_pre", "div_mipi1",
+ DIV_DISP1_0, 20, 4, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre0", "div_mmc0",
+ DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre1", "div_mmc1",
+ DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre2", "div_mmc2",
+ DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_mmc_pre3", "div_mmc3",
+ DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_spi_pre0", "div_spi0",
+ DIV_PERIC1, 8, 8, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_spi_pre1", "div_spi1",
+ DIV_PERIC1, 24, 8, CLK_SET_RATE_PARENT, 0),
+ DIV_F(none, "div_spi_pre2", "div_spi2",
+ DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0),
+};
+
+struct samsung_gate_clock exynos5250_gate_clks[] __initdata = {
+ GATE(gscl0, "gscl0", "none", GATE_IP_GSCL, 0, 0, 0),
+ GATE(gscl1, "gscl1", "none", GATE_IP_GSCL, 1, 0, 0),
+ GATE(gscl2, "gscl2", "aclk266", GATE_IP_GSCL, 2, 0, 0),
+ GATE(gscl3, "gscl3", "aclk266", GATE_IP_GSCL, 3, 0, 0),
+ GATE(gscl_wa, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0),
+ GATE(gscl_wb, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0),
+ GATE(smmu_gscl0, "smmu_gscl0", "aclk266", GATE_IP_GSCL, 7, 0, 0),
+ GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0),
+ GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0),
+ GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0),
+ GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0),
+ GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 2, 0, 0),
+ GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 1, 0, 0),
+ GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0),
+ GATE(jpeg, "jpeg", "aclk166", GATE_IP_GEN, 2, 0, 0),
+ GATE(mdma1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0),
+ GATE(smmu_rotator, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0),
+ GATE(smmu_jpeg, "smmu_jpeg", "aclk166", GATE_IP_GEN, 7, 0, 0),
+ GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0),
+ GATE(pdma0, "pdma0", "aclk200", GATE_IP_FSYS, 1, 0, 0),
+ GATE(pdma1, "pdma1", "aclk200", GATE_IP_FSYS, 2, 0, 0),
+ GATE(sata, "sata", "aclk200", GATE_IP_FSYS, 6, 0, 0),
+ GATE(usbotg, "usbotg", "aclk200", GATE_IP_FSYS, 7, 0, 0),
+ GATE(mipi_hsi, "mipi_hsi", "aclk200", GATE_IP_FSYS, 8, 0, 0),
+ GATE(sdmmc0, "sdmmc0", "aclk200", GATE_IP_FSYS, 12, 0, 0),
+ GATE(sdmmc1, "sdmmc1", "aclk200", GATE_IP_FSYS, 13, 0, 0),
+ GATE(sdmmc2, "sdmmc2", "aclk200", GATE_IP_FSYS, 14, 0, 0),
+ GATE(sdmmc3, "sdmmc3", "aclk200", GATE_IP_FSYS, 15, 0, 0),
+ GATE(sromc, "sromc", "aclk200", GATE_IP_FSYS, 17, 0, 0),
+ GATE(usb2, "usb2", "aclk200", GATE_IP_FSYS, 18, 0, 0),
+ GATE(usb3, "usb3", "aclk200", GATE_IP_FSYS, 19, 0, 0),
+ GATE(sata_phyctrl, "sata_phyctrl", "aclk200", GATE_IP_FSYS, 24, 0, 0),
+ GATE(sata_phyi2c, "sata_phyi2c", "aclk200", GATE_IP_FSYS, 25, 0, 0),
+ GATE(uart0, "uart0", "aclk66", GATE_IP_PERIC, 0, 0, 0),
+ GATE(uart1, "uart1", "aclk66", GATE_IP_PERIC, 1, 0, 0),
+ GATE(uart2, "uart2", "aclk66", GATE_IP_PERIC, 2, 0, 0),
+ GATE(uart3, "uart3", "aclk66", GATE_IP_PERIC, 3, 0, 0),
+ GATE(uart4, "uart4", "aclk66", GATE_IP_PERIC, 4, 0, 0),
+ GATE(i2c0, "i2c0", "aclk66", GATE_IP_PERIC, 6, 0, 0),
+ GATE(i2c1, "i2c1", "aclk66", GATE_IP_PERIC, 7, 0, 0),
+ GATE(i2c2, "i2c2", "aclk66", GATE_IP_PERIC, 8, 0, 0),
+ GATE(i2c3, "i2c3", "aclk66", GATE_IP_PERIC, 9, 0, 0),
+ GATE(i2c4, "i2c4", "aclk66", GATE_IP_PERIC, 10, 0, 0),
+ GATE(i2c5, "i2c5", "aclk66", GATE_IP_PERIC, 11, 0, 0),
+ GATE(i2c6, "i2c6", "aclk66", GATE_IP_PERIC, 12, 0, 0),
+ GATE(i2c7, "i2c7", "aclk66", GATE_IP_PERIC, 13, 0, 0),
+ GATE(i2c_hdmi, "i2c_hdmi", "aclk66", GATE_IP_PERIC, 14, 0, 0),
+ GATE(adc, "adc", "aclk66", GATE_IP_PERIC, 15, 0, 0),
+ GATE(spi0, "spi0", "aclk66", GATE_IP_PERIC, 16, 0, 0),
+ GATE(spi1, "spi1", "aclk66", GATE_IP_PERIC, 17, 0, 0),
+ GATE(spi2, "spi2", "aclk66", GATE_IP_PERIC, 18, 0, 0),
+ GATE(i2s1, "i2s1", "aclk66", GATE_IP_PERIC, 20, 0, 0),
+ GATE(i2s2, "i2s2", "aclk66", GATE_IP_PERIC, 21, 0, 0),
+ GATE(pcm1, "pcm1", "aclk66", GATE_IP_PERIC, 22, 0, 0),
+ GATE(pcm2, "pcm2", "aclk66", GATE_IP_PERIC, 23, 0, 0),
+ GATE(pwm, "pwm", "aclk66", GATE_IP_PERIC, 24, 0, 0),
+ GATE(spdif, "spdif", "aclk66", GATE_IP_PERIC, 26, 0, 0),
+ GATE(ac97, "ac97", "aclk66", GATE_IP_PERIC, 27, 0, 0),
+ GATE(hsi2c0, "hsi2c0", "aclk66", GATE_IP_PERIC, 28, 0, 0),
+ GATE(hsi2c1, "hsi2c1", "aclk66", GATE_IP_PERIC, 29, 0, 0),
+ GATE(hsi2c2, "hsi2c2", "aclk66", GATE_IP_PERIC, 30, 0, 0),
+ GATE(hsi2c3, "hsi2c3", "aclk66", GATE_IP_PERIC, 31, 0, 0),
+ GATE(chipid, "chipid", "aclk66", GATE_IP_PERIS, 0, 0, 0),
+ GATE(sysreg, "sysreg", "aclk66",
+ GATE_IP_PERIS, 1, CLK_IGNORE_UNUSED, 0),
+ GATE(pmu, "pmu", "aclk66", GATE_IP_PERIS, 2, CLK_IGNORE_UNUSED, 0),
+ GATE(tzpc0, "tzpc0", "aclk66", GATE_IP_PERIS, 6, 0, 0),
+ GATE(tzpc1, "tzpc1", "aclk66", GATE_IP_PERIS, 7, 0, 0),
+ GATE(tzpc2, "tzpc2", "aclk66", GATE_IP_PERIS, 8, 0, 0),
+ GATE(tzpc3, "tzpc3", "aclk66", GATE_IP_PERIS, 9, 0, 0),
+ GATE(tzpc4, "tzpc4", "aclk66", GATE_IP_PERIS, 10, 0, 0),
+ GATE(tzpc5, "tzpc5", "aclk66", GATE_IP_PERIS, 11, 0, 0),
+ GATE(tzpc6, "tzpc6", "aclk66", GATE_IP_PERIS, 12, 0, 0),
+ GATE(tzpc7, "tzpc7", "aclk66", GATE_IP_PERIS, 13, 0, 0),
+ GATE(tzpc8, "tzpc8", "aclk66", GATE_IP_PERIS, 14, 0, 0),
+ GATE(tzpc9, "tzpc9", "aclk66", GATE_IP_PERIS, 15, 0, 0),
+ GATE(hdmi_cec, "hdmi_cec", "aclk66", GATE_IP_PERIS, 16, 0, 0),
+ GATE(mct, "mct", "aclk66", GATE_IP_PERIS, 18, 0, 0),
+ GATE(wdt, "wdt", "aclk66", GATE_IP_PERIS, 19, 0, 0),
+ GATE(rtc, "rtc", "aclk66", GATE_IP_PERIS, 20, 0, 0),
+ GATE(tmu, "tmu", "aclk66", GATE_IP_PERIS, 21, 0, 0),
+ GATE(cmu_top, "cmu_top", "aclk66",
+ GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0),
+ GATE(cmu_core, "cmu_core", "aclk66",
+ GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0),
+ GATE(cmu_mem, "cmu_mem", "aclk66",
+ GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0),
+ GATE(sclk_cam_bayer, "sclk_cam_bayer", "div_cam_bayer",
+ SRC_MASK_GSCL, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_cam0, "sclk_cam0", "div_cam0",
+ SRC_MASK_GSCL, 16, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_cam1, "sclk_cam1", "div_cam1",
+ SRC_MASK_GSCL, 20, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_gscl_wa, "sclk_gscl_wa", "div_gscl_wa",
+ SRC_MASK_GSCL, 24, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_gscl_wb, "sclk_gscl_wb", "div_gscl_wb",
+ SRC_MASK_GSCL, 28, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_fimd1, "sclk_fimd1", "div_fimd1",
+ SRC_MASK_DISP1_0, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mipi1, "sclk_mipi1", "div_mipi1",
+ SRC_MASK_DISP1_0, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_dp, "sclk_dp", "div_dp",
+ SRC_MASK_DISP1_0, 16, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi",
+ SRC_MASK_DISP1_0, 20, 0, 0),
+ GATE(sclk_audio0, "sclk_audio0", "div_audio0",
+ SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mmc0, "sclk_mmc0", "div_mmc_pre0",
+ SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mmc1, "sclk_mmc1", "div_mmc_pre1",
+ SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mmc2, "sclk_mmc2", "div_mmc_pre2",
+ SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_mmc3, "sclk_mmc3", "div_mmc_pre3",
+ SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_sata, "sclk_sata", "div_sata",
+ SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_usb3, "sclk_usb3", "div_usb3",
+ SRC_MASK_FSYS, 28, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_jpeg, "sclk_jpeg", "div_jpeg",
+ SRC_MASK_GEN, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_uart0, "sclk_uart0", "div_uart0",
+ SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_uart1, "sclk_uart1", "div_uart1",
+ SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_uart2, "sclk_uart2", "div_uart2",
+ SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_uart3, "sclk_uart3", "div_uart3",
+ SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_pwm, "sclk_pwm", "div_pwm",
+ SRC_MASK_PERIC0, 24, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_audio1, "sclk_audio1", "div_audio1",
+ SRC_MASK_PERIC1, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_audio2, "sclk_audio2", "div_audio2",
+ SRC_MASK_PERIC1, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_spdif, "sclk_spdif", "mout_spdif",
+ SRC_MASK_PERIC1, 4, 0, 0),
+ GATE(sclk_spi0, "sclk_spi0", "div_spi_pre0",
+ SRC_MASK_PERIC1, 16, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_spi1, "sclk_spi1", "div_spi_pre1",
+ SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0),
+ GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2",
+ SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0),
+ GATE(fimd1, "fimd1", "aclk200", GATE_IP_DISP1, 0, 0, 0),
+ GATE(mie1, "mie1", "aclk200", GATE_IP_DISP1, 1, 0, 0),
+ GATE(dsim0, "dsim0", "aclk200", GATE_IP_DISP1, 3, 0, 0),
+ GATE(dp, "dp", "aclk200", GATE_IP_DISP1, 4, 0, 0),
+ GATE(mixer, "mixer", "aclk200", GATE_IP_DISP1, 5, 0, 0),
+ GATE(hdmi, "hdmi", "aclk200", GATE_IP_DISP1, 6, 0, 0),
+};
+
+static __initdata struct of_device_id ext_clk_match[] = {
+ { .compatible = "samsung,clock-xxti", .data = (void *)0, },
+ { },
+};
+
+/* register exynox5250 clocks */
+void __init exynos5250_clk_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+ struct clk *apll, *mpll, *epll, *vpll, *bpll, *gpll, *cpll;
+
+ if (np) {
+ reg_base = of_iomap(np, 0);
+ if (!reg_base)
+ panic("%s: failed to map registers\n", __func__);
+ } else {
+ panic("%s: unable to determine soc\n", __func__);
+ }
+
+ samsung_clk_init(np, reg_base, nr_clks,
+ exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs),
+ NULL, 0);
+ samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks,
+ ARRAY_SIZE(exynos5250_fixed_rate_ext_clks),
+ ext_clk_match);
+
+ apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll",
+ reg_base + 0x100);
+ mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll",
+ reg_base + 0x4100);
+ bpll = samsung_clk_register_pll35xx("fout_bpll", "fin_pll",
+ reg_base + 0x20110);
+ gpll = samsung_clk_register_pll35xx("fout_gpll", "fin_pll",
+ reg_base + 0x10150);
+ cpll = samsung_clk_register_pll35xx("fout_cpll", "fin_pll",
+ reg_base + 0x10120);
+ epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll",
+ reg_base + 0x10130);
+ vpll = samsung_clk_register_pll36xx("fout_vpll", "mout_vpllsrc",
+ reg_base + 0x10140);
+
+ samsung_clk_register_fixed_rate(exynos5250_fixed_rate_clks,
+ ARRAY_SIZE(exynos5250_fixed_rate_clks));
+ samsung_clk_register_fixed_factor(exynos5250_fixed_factor_clks,
+ ARRAY_SIZE(exynos5250_fixed_factor_clks));
+ samsung_clk_register_mux(exynos5250_mux_clks,
+ ARRAY_SIZE(exynos5250_mux_clks));
+ samsung_clk_register_div(exynos5250_div_clks,
+ ARRAY_SIZE(exynos5250_div_clks));
+ samsung_clk_register_gate(exynos5250_gate_clks,
+ ARRAY_SIZE(exynos5250_gate_clks));
+
+ pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
+ _get_rate("armclk"));
+}
+CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
new file mode 100644
index 000000000..7d5434167
--- /dev/null
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Author: Thomas Abraham <thomas.ab@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 Exynos5440 SoC.
+*/
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk.h"
+#include "clk-pll.h"
+
+#define CLKEN_OV_VAL 0xf8
+#define CPU_CLK_STATUS 0xfc
+#define MISC_DOUT1 0x558
+
+/*
+ * Let each supported clock get a unique id. This id is used to lookup the clock
+ * for device tree based platforms.
+ */
+enum exynos5440_clks {
+ none, xtal, arm_clk,
+
+ spi_baud = 16, pb0_250, pr0_250, pr1_250, b_250, b_125, b_200, sata,
+ usb, gmac0, cs250, pb0_250_o, pr0_250_o, pr1_250_o, b_250_o, b_125_o,
+ b_200_o, sata_o, usb_o, gmac0_o, cs250_o,
+
+ nr_clks,
+};
+
+/* parent clock name list */
+PNAME(mout_armclk_p) = { "cplla", "cpllb" };
+PNAME(mout_spi_p) = { "div125", "div200" };
+
+/* fixed rate clocks generated outside the soc */
+struct samsung_fixed_rate_clock exynos5440_fixed_rate_ext_clks[] __initdata = {
+ FRATE(none, "xtal", NULL, CLK_IS_ROOT, 0),
+};
+
+/* fixed rate clocks */
+struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = {
+ FRATE(none, "ppll", NULL, CLK_IS_ROOT, 1000000000),
+ FRATE(none, "usb_phy0", NULL, CLK_IS_ROOT, 60000000),
+ FRATE(none, "usb_phy1", NULL, CLK_IS_ROOT, 60000000),
+ FRATE(none, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000),
+ FRATE(none, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000),
+};
+
+/* fixed factor clocks */
+struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = {
+ FFACTOR(none, "div250", "ppll", 1, 4, 0),
+ FFACTOR(none, "div200", "ppll", 1, 5, 0),
+ FFACTOR(none, "div125", "div250", 1, 2, 0),
+};
+
+/* mux clocks */
+struct samsung_mux_clock exynos5440_mux_clks[] __initdata = {
+ MUX(none, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1),
+ MUX_A(arm_clk, "arm_clk", mout_armclk_p,
+ CPU_CLK_STATUS, 0, 1, "armclk"),
+};
+
+/* divider clocks */
+struct samsung_div_clock exynos5440_div_clks[] __initdata = {
+ DIV(spi_baud, "div_spi", "mout_spi", MISC_DOUT1, 3, 2),
+};
+
+/* gate clocks */
+struct samsung_gate_clock exynos5440_gate_clks[] __initdata = {
+ GATE(pb0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0),
+ GATE(pr0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0),
+ GATE(pr1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0),
+ GATE(b_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0),
+ GATE(b_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0),
+ GATE(b_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0),
+ GATE(sata, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0),
+ GATE(usb, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0),
+ GATE(gmac0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0),
+ GATE(cs250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0),
+ GATE(pb0_250_o, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0),
+ GATE(pr0_250_o, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0),
+ GATE(pr1_250_o, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0),
+ GATE(b_250_o, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0),
+ GATE(b_125_o, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0),
+ GATE(b_200_o, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0),
+ GATE(sata_o, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0),
+ GATE(usb_o, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0),
+ GATE(gmac0_o, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0),
+ GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0),
+};
+
+static __initdata struct of_device_id ext_clk_match[] = {
+ { .compatible = "samsung,clock-xtal", .data = (void *)0, },
+ {},
+};
+
+/* register exynos5440 clocks */
+void __init exynos5440_clk_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: failed to map clock controller registers,"
+ " aborting clock initialization\n", __func__);
+ return;
+ }
+
+ samsung_clk_init(np, reg_base, nr_clks, NULL, 0, NULL, 0);
+ samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
+ ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
+
+ samsung_clk_register_pll2550x("cplla", "xtal", reg_base + 0x1c, 0x10);
+ samsung_clk_register_pll2550x("cpllb", "xtal", reg_base + 0x20, 0x10);
+
+ samsung_clk_register_fixed_rate(exynos5440_fixed_rate_clks,
+ ARRAY_SIZE(exynos5440_fixed_rate_clks));
+ samsung_clk_register_fixed_factor(exynos5440_fixed_factor_clks,
+ ARRAY_SIZE(exynos5440_fixed_factor_clks));
+ samsung_clk_register_mux(exynos5440_mux_clks,
+ ARRAY_SIZE(exynos5440_mux_clks));
+ samsung_clk_register_div(exynos5440_div_clks,
+ ARRAY_SIZE(exynos5440_div_clks));
+ samsung_clk_register_gate(exynos5440_gate_clks,
+ ARRAY_SIZE(exynos5440_gate_clks));
+
+ pr_info("Exynos5440: arm_clk = %ldHz\n", _get_rate("armclk"));
+ pr_info("exynos5440 clock initialization complete\n");
+}
+CLK_OF_DECLARE(exynos5440_clk, "samsung,exynos5440-clock", exynos5440_clk_init);
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
new file mode 100644
index 000000000..362f12dcd
--- /dev/null
+++ b/drivers/clk/samsung/clk-pll.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ *
+ * 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.
+ *
+ * This file contains the utility functions to register the pll clocks.
+*/
+
+#include <linux/errno.h>
+#include "clk.h"
+#include "clk-pll.h"
+
+/*
+ * PLL35xx Clock Type
+ */
+
+#define PLL35XX_MDIV_MASK (0x3FF)
+#define PLL35XX_PDIV_MASK (0x3F)
+#define PLL35XX_SDIV_MASK (0x7)
+#define PLL35XX_MDIV_SHIFT (16)
+#define PLL35XX_PDIV_SHIFT (8)
+#define PLL35XX_SDIV_SHIFT (0)
+
+struct samsung_clk_pll35xx {
+ struct clk_hw hw;
+ const void __iomem *con_reg;
+};
+
+#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw)
+
+static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw);
+ u32 mdiv, pdiv, sdiv, pll_con;
+ u64 fvco = parent_rate;
+
+ pll_con = __raw_readl(pll->con_reg);
+ mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
+ pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
+ sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
+
+ fvco *= mdiv;
+ do_div(fvco, (pdiv << sdiv));
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll35xx_clk_ops = {
+ .recalc_rate = samsung_pll35xx_recalc_rate,
+};
+
+struct clk * __init samsung_clk_register_pll35xx(const char *name,
+ const char *pname, const void __iomem *con_reg)
+{
+ struct samsung_clk_pll35xx *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &samsung_pll35xx_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->con_reg = con_reg;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ if (clk_register_clkdev(clk, name, NULL))
+ pr_err("%s: failed to register lookup for %s", __func__, name);
+
+ return clk;
+}
+
+/*
+ * PLL36xx Clock Type
+ */
+
+#define PLL36XX_KDIV_MASK (0xFFFF)
+#define PLL36XX_MDIV_MASK (0x1FF)
+#define PLL36XX_PDIV_MASK (0x3F)
+#define PLL36XX_SDIV_MASK (0x7)
+#define PLL36XX_MDIV_SHIFT (16)
+#define PLL36XX_PDIV_SHIFT (8)
+#define PLL36XX_SDIV_SHIFT (0)
+
+struct samsung_clk_pll36xx {
+ struct clk_hw hw;
+ const void __iomem *con_reg;
+};
+
+#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw)
+
+static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw);
+ u32 mdiv, pdiv, sdiv, pll_con0, pll_con1;
+ s16 kdiv;
+ u64 fvco = parent_rate;
+
+ pll_con0 = __raw_readl(pll->con_reg);
+ pll_con1 = __raw_readl(pll->con_reg + 4);
+ mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
+ pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
+ sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
+ kdiv = (s16)(pll_con1 & PLL36XX_KDIV_MASK);
+
+ fvco *= (mdiv << 16) + kdiv;
+ do_div(fvco, (pdiv << sdiv));
+ fvco >>= 16;
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll36xx_clk_ops = {
+ .recalc_rate = samsung_pll36xx_recalc_rate,
+};
+
+struct clk * __init samsung_clk_register_pll36xx(const char *name,
+ const char *pname, const void __iomem *con_reg)
+{
+ struct samsung_clk_pll36xx *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &samsung_pll36xx_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->con_reg = con_reg;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ if (clk_register_clkdev(clk, name, NULL))
+ pr_err("%s: failed to register lookup for %s", __func__, name);
+
+ return clk;
+}
+
+/*
+ * PLL45xx Clock Type
+ */
+
+#define PLL45XX_MDIV_MASK (0x3FF)
+#define PLL45XX_PDIV_MASK (0x3F)
+#define PLL45XX_SDIV_MASK (0x7)
+#define PLL45XX_MDIV_SHIFT (16)
+#define PLL45XX_PDIV_SHIFT (8)
+#define PLL45XX_SDIV_SHIFT (0)
+
+struct samsung_clk_pll45xx {
+ struct clk_hw hw;
+ enum pll45xx_type type;
+ const void __iomem *con_reg;
+};
+
+#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
+
+static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
+ u32 mdiv, pdiv, sdiv, pll_con;
+ u64 fvco = parent_rate;
+
+ pll_con = __raw_readl(pll->con_reg);
+ mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
+ pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
+ sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
+
+ if (pll->type == pll_4508)
+ sdiv = sdiv - 1;
+
+ fvco *= mdiv;
+ do_div(fvco, (pdiv << sdiv));
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll45xx_clk_ops = {
+ .recalc_rate = samsung_pll45xx_recalc_rate,
+};
+
+struct clk * __init samsung_clk_register_pll45xx(const char *name,
+ const char *pname, const void __iomem *con_reg,
+ enum pll45xx_type type)
+{
+ struct samsung_clk_pll45xx *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &samsung_pll45xx_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->con_reg = con_reg;
+ pll->type = type;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ if (clk_register_clkdev(clk, name, NULL))
+ pr_err("%s: failed to register lookup for %s", __func__, name);
+
+ return clk;
+}
+
+/*
+ * PLL46xx Clock Type
+ */
+
+#define PLL46XX_MDIV_MASK (0x1FF)
+#define PLL46XX_PDIV_MASK (0x3F)
+#define PLL46XX_SDIV_MASK (0x7)
+#define PLL46XX_MDIV_SHIFT (16)
+#define PLL46XX_PDIV_SHIFT (8)
+#define PLL46XX_SDIV_SHIFT (0)
+
+#define PLL46XX_KDIV_MASK (0xFFFF)
+#define PLL4650C_KDIV_MASK (0xFFF)
+#define PLL46XX_KDIV_SHIFT (0)
+
+struct samsung_clk_pll46xx {
+ struct clk_hw hw;
+ enum pll46xx_type type;
+ const void __iomem *con_reg;
+};
+
+#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw)
+
+static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw);
+ u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
+ u64 fvco = parent_rate;
+
+ pll_con0 = __raw_readl(pll->con_reg);
+ pll_con1 = __raw_readl(pll->con_reg + 4);
+ mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
+ pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
+ sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
+ kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK :
+ pll_con1 & PLL46XX_KDIV_MASK;
+
+ shift = pll->type == pll_4600 ? 16 : 10;
+ fvco *= (mdiv << shift) + kdiv;
+ do_div(fvco, (pdiv << sdiv));
+ fvco >>= shift;
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll46xx_clk_ops = {
+ .recalc_rate = samsung_pll46xx_recalc_rate,
+};
+
+struct clk * __init samsung_clk_register_pll46xx(const char *name,
+ const char *pname, const void __iomem *con_reg,
+ enum pll46xx_type type)
+{
+ struct samsung_clk_pll46xx *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &samsung_pll46xx_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->con_reg = con_reg;
+ pll->type = type;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ if (clk_register_clkdev(clk, name, NULL))
+ pr_err("%s: failed to register lookup for %s", __func__, name);
+
+ return clk;
+}
+
+/*
+ * PLL2550x Clock Type
+ */
+
+#define PLL2550X_R_MASK (0x1)
+#define PLL2550X_P_MASK (0x3F)
+#define PLL2550X_M_MASK (0x3FF)
+#define PLL2550X_S_MASK (0x7)
+#define PLL2550X_R_SHIFT (20)
+#define PLL2550X_P_SHIFT (14)
+#define PLL2550X_M_SHIFT (4)
+#define PLL2550X_S_SHIFT (0)
+
+struct samsung_clk_pll2550x {
+ struct clk_hw hw;
+ const void __iomem *reg_base;
+ unsigned long offset;
+};
+
+#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
+
+static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
+ u32 r, p, m, s, pll_stat;
+ u64 fvco = parent_rate;
+
+ pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
+ r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
+ if (!r)
+ return 0;
+ p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
+ m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
+ s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
+
+ fvco *= m;
+ do_div(fvco, (p << s));
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll2550x_clk_ops = {
+ .recalc_rate = samsung_pll2550x_recalc_rate,
+};
+
+struct clk * __init samsung_clk_register_pll2550x(const char *name,
+ const char *pname, const void __iomem *reg_base,
+ const unsigned long offset)
+{
+ struct samsung_clk_pll2550x *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &samsung_pll2550x_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->reg_base = reg_base;
+ pll->offset = offset;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ if (clk_register_clkdev(clk, name, NULL))
+ pr_err("%s: failed to register lookup for %s", __func__, name);
+
+ return clk;
+}
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h
new file mode 100644
index 000000000..f33786e9a
--- /dev/null
+++ b/drivers/clk/samsung/clk-pll.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ *
+ * 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 all PLL's in Samsung platforms
+*/
+
+#ifndef __SAMSUNG_CLK_PLL_H
+#define __SAMSUNG_CLK_PLL_H
+
+enum pll45xx_type {
+ pll_4500,
+ pll_4502,
+ pll_4508
+};
+
+enum pll46xx_type {
+ pll_4600,
+ pll_4650,
+ pll_4650c,
+};
+
+extern struct clk * __init samsung_clk_register_pll35xx(const char *name,
+ const char *pname, const void __iomem *con_reg);
+extern struct clk * __init samsung_clk_register_pll36xx(const char *name,
+ const char *pname, const void __iomem *con_reg);
+extern struct clk * __init samsung_clk_register_pll45xx(const char *name,
+ const char *pname, const void __iomem *con_reg,
+ enum pll45xx_type type);
+extern struct clk * __init samsung_clk_register_pll46xx(const char *name,
+ const char *pname, const void __iomem *con_reg,
+ enum pll46xx_type type);
+extern struct clk * __init samsung_clk_register_pll2550x(const char *name,
+ const char *pname, const void __iomem *reg_base,
+ const unsigned long offset);
+
+#endif /* __SAMSUNG_CLK_PLL_H */
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
new file mode 100644
index 000000000..cd3c40ab5
--- /dev/null
+++ b/drivers/clk/samsung/clk.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab@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.
+ *
+ * This file includes utility functions to register clocks to common
+ * clock framework for Samsung platforms.
+*/
+
+#include <linux/syscore_ops.h>
+#include "clk.h"
+
+static DEFINE_SPINLOCK(lock);
+static struct clk **clk_table;
+static void __iomem *reg_base;
+#ifdef CONFIG_OF
+static struct clk_onecell_data clk_data;
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *reg_dump;
+static unsigned long nr_reg_dump;
+
+static int samsung_clk_suspend(void)
+{
+ struct samsung_clk_reg_dump *rd = reg_dump;
+ unsigned long i;
+
+ for (i = 0; i < nr_reg_dump; i++, rd++)
+ rd->value = __raw_readl(reg_base + rd->offset);
+
+ return 0;
+}
+
+static void samsung_clk_resume(void)
+{
+ struct samsung_clk_reg_dump *rd = reg_dump;
+ unsigned long i;
+
+ for (i = 0; i < nr_reg_dump; i++, rd++)
+ __raw_writel(rd->value, reg_base + rd->offset);
+}
+
+static struct syscore_ops samsung_clk_syscore_ops = {
+ .suspend = samsung_clk_suspend,
+ .resume = samsung_clk_resume,
+};
+#endif /* CONFIG_PM_SLEEP */
+
+/* setup the essentials required to support clock lookup using ccf */
+void __init samsung_clk_init(struct device_node *np, void __iomem *base,
+ unsigned long nr_clks, unsigned long *rdump,
+ unsigned long nr_rdump, unsigned long *soc_rdump,
+ unsigned long nr_soc_rdump)
+{
+ reg_base = base;
+
+#ifdef CONFIG_PM_SLEEP
+ if (rdump && nr_rdump) {
+ unsigned int idx;
+ reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
+ * (nr_rdump + nr_soc_rdump), GFP_KERNEL);
+ if (!reg_dump) {
+ pr_err("%s: memory alloc for register dump failed\n",
+ __func__);
+ return;
+ }
+
+ for (idx = 0; idx < nr_rdump; idx++)
+ reg_dump[idx].offset = rdump[idx];
+ for (idx = 0; idx < nr_soc_rdump; idx++)
+ reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
+ nr_reg_dump = nr_rdump + nr_soc_rdump;
+ register_syscore_ops(&samsung_clk_syscore_ops);
+ }
+#endif
+
+ clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
+ if (!clk_table)
+ panic("could not allocate clock lookup table\n");
+
+ if (!np)
+ return;
+
+#ifdef CONFIG_OF
+ clk_data.clks = clk_table;
+ clk_data.clk_num = nr_clks;
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+#endif
+}
+
+/* add a clock instance to the clock lookup table used for dt based lookup */
+void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
+{
+ if (clk_table && id)
+ clk_table[id] = clk;
+}
+
+/* register a list of aliases */
+void __init samsung_clk_register_alias(struct samsung_clock_alias *list,
+ unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx, ret;
+
+ if (!clk_table) {
+ pr_err("%s: clock table missing\n", __func__);
+ return;
+ }
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ if (!list->id) {
+ pr_err("%s: clock id missing for index %d\n", __func__,
+ idx);
+ continue;
+ }
+
+ clk = clk_table[list->id];
+ if (!clk) {
+ pr_err("%s: failed to find clock %d\n", __func__,
+ list->id);
+ continue;
+ }
+
+ ret = clk_register_clkdev(clk, list->alias, list->dev_name);
+ if (ret)
+ pr_err("%s: failed to register lookup %s\n",
+ __func__, list->alias);
+ }
+}
+
+/* register a list of fixed clocks */
+void __init samsung_clk_register_fixed_rate(
+ struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx, ret;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ clk = clk_register_fixed_rate(NULL, list->name,
+ list->parent_name, list->flags, list->fixed_rate);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ list->name);
+ continue;
+ }
+
+ samsung_clk_add_lookup(clk, list->id);
+
+ /*
+ * Unconditionally add a clock lookup for the fixed rate clocks.
+ * There are not many of these on any of Samsung platforms.
+ */
+ ret = clk_register_clkdev(clk, list->name, NULL);
+ if (ret)
+ pr_err("%s: failed to register clock lookup for %s",
+ __func__, list->name);
+ }
+}
+
+/* register a list of fixed factor clocks */
+void __init samsung_clk_register_fixed_factor(
+ struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ clk = clk_register_fixed_factor(NULL, list->name,
+ list->parent_name, list->flags, list->mult, list->div);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ list->name);
+ continue;
+ }
+
+ samsung_clk_add_lookup(clk, list->id);
+ }
+}
+
+/* register a list of mux clocks */
+void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
+ unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx, ret;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ clk = clk_register_mux(NULL, list->name, list->parent_names,
+ list->num_parents, list->flags, reg_base + list->offset,
+ list->shift, list->width, list->mux_flags, &lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ list->name);
+ continue;
+ }
+
+ samsung_clk_add_lookup(clk, list->id);
+
+ /* register a clock lookup only if a clock alias is specified */
+ if (list->alias) {
+ ret = clk_register_clkdev(clk, list->alias,
+ list->dev_name);
+ if (ret)
+ pr_err("%s: failed to register lookup %s\n",
+ __func__, list->alias);
+ }
+ }
+}
+
+/* register a list of div clocks */
+void __init samsung_clk_register_div(struct samsung_div_clock *list,
+ unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx, ret;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ if (list->table)
+ clk = clk_register_divider_table(NULL, list->name,
+ list->parent_name, list->flags,
+ reg_base + list->offset, list->shift,
+ list->width, list->div_flags,
+ list->table, &lock);
+ else
+ clk = clk_register_divider(NULL, list->name,
+ list->parent_name, list->flags,
+ reg_base + list->offset, list->shift,
+ list->width, list->div_flags, &lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ list->name);
+ continue;
+ }
+
+ samsung_clk_add_lookup(clk, list->id);
+
+ /* register a clock lookup only if a clock alias is specified */
+ if (list->alias) {
+ ret = clk_register_clkdev(clk, list->alias,
+ list->dev_name);
+ if (ret)
+ pr_err("%s: failed to register lookup %s\n",
+ __func__, list->alias);
+ }
+ }
+}
+
+/* register a list of gate clocks */
+void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
+ unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx, ret;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ clk = clk_register_gate(NULL, list->name, list->parent_name,
+ list->flags, reg_base + list->offset,
+ list->bit_idx, list->gate_flags, &lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ list->name);
+ continue;
+ }
+
+ /* register a clock lookup only if a clock alias is specified */
+ if (list->alias) {
+ ret = clk_register_clkdev(clk, list->alias,
+ list->dev_name);
+ if (ret)
+ pr_err("%s: failed to register lookup %s\n",
+ __func__, list->alias);
+ }
+
+ samsung_clk_add_lookup(clk, list->id);
+ }
+}
+
+/*
+ * obtain the clock speed of all external fixed clock sources from device
+ * tree and register it
+ */
+#ifdef CONFIG_OF
+void __init samsung_clk_of_register_fixed_ext(
+ struct samsung_fixed_rate_clock *fixed_rate_clk,
+ unsigned int nr_fixed_rate_clk,
+ struct of_device_id *clk_matches)
+{
+ const struct of_device_id *match;
+ struct device_node *np;
+ u32 freq;
+
+ for_each_matching_node_and_match(np, clk_matches, &match) {
+ if (of_property_read_u32(np, "clock-frequency", &freq))
+ continue;
+ fixed_rate_clk[(u32)match->data].fixed_rate = freq;
+ }
+ samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
+}
+#endif
+
+/* utility function to get the rate of a specified clock */
+unsigned long _get_rate(const char *clk_name)
+{
+ struct clk *clk;
+ unsigned long rate;
+
+ clk = clk_get(NULL, clk_name);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not find clock %s\n", __func__, clk_name);
+ return 0;
+ }
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+ return rate;
+}
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
new file mode 100644
index 000000000..e4ad6ea9a
--- /dev/null
+++ b/drivers/clk/samsung/clk.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab@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 all Samsung platforms
+*/
+
+#ifndef __SAMSUNG_CLK_H
+#define __SAMSUNG_CLK_H
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+/**
+ * struct samsung_clock_alias: information about mux clock
+ * @id: platform specific id of the clock.
+ * @dev_name: name of the device to which this clock belongs.
+ * @alias: optional clock alias name to be assigned to this clock.
+ */
+struct samsung_clock_alias {
+ unsigned int id;
+ const char *dev_name;
+ const char *alias;
+};
+
+#define ALIAS(_id, dname, a) \
+ { \
+ .id = _id, \
+ .dev_name = dname, \
+ .alias = a, \
+ }
+
+/**
+ * struct samsung_fixed_rate_clock: information about fixed-rate clock
+ * @id: platform specific id of the clock.
+ * @name: name of this fixed-rate clock.
+ * @parent_name: optional parent clock name.
+ * @flags: optional fixed-rate clock flags.
+ * @fixed-rate: fixed clock rate of this clock.
+ */
+struct samsung_fixed_rate_clock {
+ unsigned int id;
+ char *name;
+ const char *parent_name;
+ unsigned long flags;
+ unsigned long fixed_rate;
+};
+
+#define FRATE(_id, cname, pname, f, frate) \
+ { \
+ .id = _id, \
+ .name = cname, \
+ .parent_name = pname, \
+ .flags = f, \
+ .fixed_rate = frate, \
+ }
+
+/*
+ * struct samsung_fixed_factor_clock: information about fixed-factor clock
+ * @id: platform specific id of the clock.
+ * @name: name of this fixed-factor clock.
+ * @parent_name: parent clock name.
+ * @mult: fixed multiplication factor.
+ * @div: fixed division factor.
+ * @flags: optional fixed-factor clock flags.
+ */
+struct samsung_fixed_factor_clock {
+ unsigned int id;
+ char *name;
+ const char *parent_name;
+ unsigned long mult;
+ unsigned long div;
+ unsigned long flags;
+};
+
+#define FFACTOR(_id, cname, pname, m, d, f) \
+ { \
+ .id = _id, \
+ .name = cname, \
+ .parent_name = pname, \
+ .mult = m, \
+ .div = d, \
+ .flags = f, \
+ }
+
+/**
+ * struct samsung_mux_clock: information about mux clock
+ * @id: platform specific id of the clock.
+ * @dev_name: name of the device to which this clock belongs.
+ * @name: name of this mux clock.
+ * @parent_names: array of pointer to parent clock names.
+ * @num_parents: number of parents listed in @parent_names.
+ * @flags: optional flags for basic clock.
+ * @offset: offset of the register for configuring the mux.
+ * @shift: starting bit location of the mux control bit-field in @reg.
+ * @width: width of the mux control bit-field in @reg.
+ * @mux_flags: flags for mux-type clock.
+ * @alias: optional clock alias name to be assigned to this clock.
+ */
+struct samsung_mux_clock {
+ unsigned int id;
+ const char *dev_name;
+ const char *name;
+ const char **parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ unsigned long offset;
+ u8 shift;
+ u8 width;
+ u8 mux_flags;
+ const char *alias;
+};
+
+#define __MUX(_id, dname, cname, pnames, o, s, w, f, mf, a) \
+ { \
+ .id = _id, \
+ .dev_name = dname, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .offset = o, \
+ .shift = s, \
+ .width = w, \
+ .mux_flags = mf, \
+ .alias = a, \
+ }
+
+#define MUX(_id, cname, pnames, o, s, w) \
+ __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, NULL)
+
+#define MUX_A(_id, cname, pnames, o, s, w, a) \
+ __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, a)
+
+#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \
+ __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL)
+
+/**
+ * @id: platform specific id of the clock.
+ * struct samsung_div_clock: information about div clock
+ * @dev_name: name of the device to which this clock belongs.
+ * @name: name of this div clock.
+ * @parent_name: name of the parent clock.
+ * @flags: optional flags for basic clock.
+ * @offset: offset of the register for configuring the div.
+ * @shift: starting bit location of the div control bit-field in @reg.
+ * @div_flags: flags for div-type clock.
+ * @alias: optional clock alias name to be assigned to this clock.
+ */
+struct samsung_div_clock {
+ unsigned int id;
+ const char *dev_name;
+ const char *name;
+ const char *parent_name;
+ unsigned long flags;
+ unsigned long offset;
+ u8 shift;
+ u8 width;
+ u8 div_flags;
+ const char *alias;
+ struct clk_div_table *table;
+};
+
+#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a, t) \
+ { \
+ .id = _id, \
+ .dev_name = dname, \
+ .name = cname, \
+ .parent_name = pname, \
+ .flags = f, \
+ .offset = o, \
+ .shift = s, \
+ .width = w, \
+ .div_flags = df, \
+ .alias = a, \
+ .table = t, \
+ }
+
+#define DIV(_id, cname, pname, o, s, w) \
+ __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, NULL)
+
+#define DIV_A(_id, cname, pname, o, s, w, a) \
+ __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a, NULL)
+
+#define DIV_F(_id, cname, pname, o, s, w, f, df) \
+ __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL, NULL)
+
+#define DIV_T(_id, cname, pname, o, s, w, t) \
+ __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, t)
+
+/**
+ * struct samsung_gate_clock: information about gate clock
+ * @id: platform specific id of the clock.
+ * @dev_name: name of the device to which this clock belongs.
+ * @name: name of this gate clock.
+ * @parent_name: name of the parent clock.
+ * @flags: optional flags for basic clock.
+ * @offset: offset of the register for configuring the gate.
+ * @bit_idx: bit index of the gate control bit-field in @reg.
+ * @gate_flags: flags for gate-type clock.
+ * @alias: optional clock alias name to be assigned to this clock.
+ */
+struct samsung_gate_clock {
+ unsigned int id;
+ const char *dev_name;
+ const char *name;
+ const char *parent_name;
+ unsigned long flags;
+ unsigned long offset;
+ u8 bit_idx;
+ u8 gate_flags;
+ const char *alias;
+};
+
+#define __GATE(_id, dname, cname, pname, o, b, f, gf, a) \
+ { \
+ .id = _id, \
+ .dev_name = dname, \
+ .name = cname, \
+ .parent_name = pname, \
+ .flags = f, \
+ .offset = o, \
+ .bit_idx = b, \
+ .gate_flags = gf, \
+ .alias = a, \
+ }
+
+#define GATE(_id, cname, pname, o, b, f, gf) \
+ __GATE(_id, NULL, cname, pname, o, b, f, gf, NULL)
+
+#define GATE_A(_id, cname, pname, o, b, f, gf, a) \
+ __GATE(_id, NULL, cname, pname, o, b, f, gf, a)
+
+#define GATE_D(_id, dname, cname, pname, o, b, f, gf) \
+ __GATE(_id, dname, cname, pname, o, b, f, gf, NULL)
+
+#define GATE_DA(_id, dname, cname, pname, o, b, f, gf, a) \
+ __GATE(_id, dname, cname, pname, o, b, f, gf, a)
+
+#define PNAME(x) static const char *x[] __initdata
+
+/**
+ * struct samsung_clk_reg_dump: register dump of clock controller registers.
+ * @offset: clock register offset from the controller base address.
+ * @value: the value to be register at offset.
+ */
+struct samsung_clk_reg_dump {
+ u32 offset;
+ u32 value;
+};
+
+extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
+ unsigned long nr_clks, unsigned long *rdump,
+ unsigned long nr_rdump, unsigned long *soc_rdump,
+ unsigned long nr_soc_rdump);
+extern void __init samsung_clk_of_register_fixed_ext(
+ struct samsung_fixed_rate_clock *fixed_rate_clk,
+ unsigned int nr_fixed_rate_clk,
+ struct of_device_id *clk_matches);
+
+extern void samsung_clk_add_lookup(struct clk *clk, unsigned int id);
+
+extern void samsung_clk_register_alias(struct samsung_clock_alias *list,
+ unsigned int nr_clk);
+extern void __init samsung_clk_register_fixed_rate(
+ struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk);
+extern void __init samsung_clk_register_fixed_factor(
+ struct samsung_fixed_factor_clock *list, unsigned int nr_clk);
+extern void __init samsung_clk_register_mux(struct samsung_mux_clock *clk_list,
+ unsigned int nr_clk);
+extern void __init samsung_clk_register_div(struct samsung_div_clock *clk_list,
+ unsigned int nr_clk);
+extern void __init samsung_clk_register_gate(
+ struct samsung_gate_clock *clk_list, unsigned int nr_clk);
+
+extern unsigned long _get_rate(const char *clk_name);
+
+#endif /* __SAMSUNG_CLK_H */
diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile
new file mode 100644
index 000000000..0303c0b99
--- /dev/null
+++ b/drivers/clk/socfpga/Makefile
@@ -0,0 +1 @@
+obj-y += clk.o
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c
new file mode 100644
index 000000000..bd11315cf
--- /dev/null
+++ b/drivers/clk/socfpga/clk.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
+ *
+ * Based from clk-highbank.c
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+/* Clock Manager offsets */
+#define CLKMGR_CTRL 0x0
+#define CLKMGR_BYPASS 0x4
+
+/* Clock bypass bits */
+#define MAINPLL_BYPASS (1<<0)
+#define SDRAMPLL_BYPASS (1<<1)
+#define SDRAMPLL_SRC_BYPASS (1<<2)
+#define PERPLL_BYPASS (1<<3)
+#define PERPLL_SRC_BYPASS (1<<4)
+
+#define SOCFPGA_PLL_BG_PWRDWN 0
+#define SOCFPGA_PLL_EXT_ENA 1
+#define SOCFPGA_PLL_PWR_DOWN 2
+#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
+#define SOCFPGA_PLL_DIVF_SHIFT 3
+#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
+#define SOCFPGA_PLL_DIVQ_SHIFT 16
+
+extern void __iomem *clk_mgr_base_addr;
+
+struct socfpga_clk {
+ struct clk_gate hw;
+ char *parent_name;
+ char *clk_name;
+ u32 fixed_div;
+};
+#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
+ unsigned long divf, divq, vco_freq, reg;
+ unsigned long bypass;
+
+ reg = readl(socfpgaclk->hw.reg);
+ bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
+ if (bypass & MAINPLL_BYPASS)
+ return parent_rate;
+
+ divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
+ divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
+ vco_freq = parent_rate * (divf + 1);
+ return vco_freq / (1 + divq);
+}
+
+
+static struct clk_ops clk_pll_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
+ u32 div;
+
+ if (socfpgaclk->fixed_div)
+ div = socfpgaclk->fixed_div;
+ else
+ div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
+
+ return parent_rate / div;
+}
+
+static const struct clk_ops periclk_ops = {
+ .recalc_rate = clk_periclk_recalc_rate,
+};
+
+static __init struct clk *socfpga_clk_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 reg;
+ struct clk *clk;
+ struct socfpga_clk *socfpga_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ u32 fixed_div;
+
+ rc = of_property_read_u32(node, "reg", &reg);
+ if (WARN_ON(rc))
+ return NULL;
+
+ socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
+ if (WARN_ON(!socfpga_clk))
+ return NULL;
+
+ socfpga_clk->hw.reg = clk_mgr_base_addr + reg;
+
+ rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
+ if (rc)
+ socfpga_clk->fixed_div = 0;
+ else
+ socfpga_clk->fixed_div = fixed_div;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ socfpga_clk->hw.hw.init = &init;
+
+ if (strcmp(clk_name, "main_pll") || strcmp(clk_name, "periph_pll") ||
+ strcmp(clk_name, "sdram_pll")) {
+ socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
+ clk_pll_ops.enable = clk_gate_ops.enable;
+ clk_pll_ops.disable = clk_gate_ops.disable;
+ }
+
+ clk = clk_register(NULL, &socfpga_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(socfpga_clk);
+ return NULL;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return clk;
+}
+
+static void __init socfpga_pll_init(struct device_node *node)
+{
+ socfpga_clk_init(node, &clk_pll_ops);
+}
+CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init);
+
+static void __init socfpga_periph_init(struct device_node *node)
+{
+ socfpga_clk_init(node, &periclk_ops);
+}
+CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init);
+
+void __init socfpga_init_clocks(void)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4);
+ ret = clk_register_clkdev(clk, NULL, "smp_twd");
+ if (ret)
+ pr_err("smp_twd alias not registered\n");
+}
diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile
new file mode 100644
index 000000000..cdb425d3b
--- /dev/null
+++ b/drivers/clk/spear/Makefile
@@ -0,0 +1,10 @@
+#
+# SPEAr Clock specific Makefile
+#
+
+obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-gpt-synth.o clk-vco-pll.o
+
+obj-$(CONFIG_ARCH_SPEAR3XX) += spear3xx_clock.o
+obj-$(CONFIG_ARCH_SPEAR6XX) += spear6xx_clock.o
+obj-$(CONFIG_MACH_SPEAR1310) += spear1310_clock.o
+obj-$(CONFIG_MACH_SPEAR1340) += spear1340_clock.o
diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c
new file mode 100644
index 000000000..bdfb4421c
--- /dev/null
+++ b/drivers/clk/spear/clk-aux-synth.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Auxiliary Synthesizer clock implementation
+ */
+
+#define pr_fmt(fmt) "clk-aux-synth: " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+/*
+ * DOC: Auxiliary Synthesizer clock
+ *
+ * Aux synth gives rate for different values of eq, x and y
+ *
+ * Fout from synthesizer can be given from two equations:
+ * Fout1 = (Fin * X/Y)/2 EQ1
+ * Fout2 = Fin * X/Y EQ2
+ */
+
+#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw)
+
+static struct aux_clk_masks default_aux_masks = {
+ .eq_sel_mask = AUX_EQ_SEL_MASK,
+ .eq_sel_shift = AUX_EQ_SEL_SHIFT,
+ .eq1_mask = AUX_EQ1_SEL,
+ .eq2_mask = AUX_EQ2_SEL,
+ .xscale_sel_mask = AUX_XSCALE_MASK,
+ .xscale_sel_shift = AUX_XSCALE_SHIFT,
+ .yscale_sel_mask = AUX_YSCALE_MASK,
+ .yscale_sel_shift = AUX_YSCALE_SHIFT,
+ .enable_bit = AUX_SYNT_ENB,
+};
+
+static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate,
+ int index)
+{
+ struct clk_aux *aux = to_clk_aux(hw);
+ struct aux_rate_tbl *rtbl = aux->rtbl;
+ u8 eq = rtbl[index].eq ? 1 : 2;
+
+ return (((prate / 10000) * rtbl[index].xscale) /
+ (rtbl[index].yscale * eq)) * 10000;
+}
+
+static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ struct clk_aux *aux = to_clk_aux(hw);
+ int unused;
+
+ return clk_round_rate_index(hw, drate, *prate, aux_calc_rate,
+ aux->rtbl_cnt, &unused);
+}
+
+static unsigned long clk_aux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_aux *aux = to_clk_aux(hw);
+ unsigned int num = 1, den = 1, val, eqn;
+ unsigned long flags = 0;
+
+ if (aux->lock)
+ spin_lock_irqsave(aux->lock, flags);
+
+ val = readl_relaxed(aux->reg);
+
+ if (aux->lock)
+ spin_unlock_irqrestore(aux->lock, flags);
+
+ eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask;
+ if (eqn == aux->masks->eq1_mask)
+ den = 2;
+
+ /* calculate numerator */
+ num = (val >> aux->masks->xscale_sel_shift) &
+ aux->masks->xscale_sel_mask;
+
+ /* calculate denominator */
+ den *= (val >> aux->masks->yscale_sel_shift) &
+ aux->masks->yscale_sel_mask;
+
+ if (!den)
+ return 0;
+
+ return (((parent_rate / 10000) * num) / den) * 10000;
+}
+
+/* Configures new clock rate of aux */
+static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_aux *aux = to_clk_aux(hw);
+ struct aux_rate_tbl *rtbl = aux->rtbl;
+ unsigned long val, flags = 0;
+ int i;
+
+ clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt,
+ &i);
+
+ if (aux->lock)
+ spin_lock_irqsave(aux->lock, flags);
+
+ val = readl_relaxed(aux->reg) &
+ ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift);
+ val |= (rtbl[i].eq & aux->masks->eq_sel_mask) <<
+ aux->masks->eq_sel_shift;
+ val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift);
+ val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) <<
+ aux->masks->xscale_sel_shift;
+ val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift);
+ val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) <<
+ aux->masks->yscale_sel_shift;
+ writel_relaxed(val, aux->reg);
+
+ if (aux->lock)
+ spin_unlock_irqrestore(aux->lock, flags);
+
+ return 0;
+}
+
+static struct clk_ops clk_aux_ops = {
+ .recalc_rate = clk_aux_recalc_rate,
+ .round_rate = clk_aux_round_rate,
+ .set_rate = clk_aux_set_rate,
+};
+
+struct clk *clk_register_aux(const char *aux_name, const char *gate_name,
+ const char *parent_name, unsigned long flags, void __iomem *reg,
+ struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl,
+ u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk)
+{
+ struct clk_aux *aux;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
+ pr_err("Invalid arguments passed");
+ return ERR_PTR(-EINVAL);
+ }
+
+ aux = kzalloc(sizeof(*aux), GFP_KERNEL);
+ if (!aux) {
+ pr_err("could not allocate aux clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_aux assignments */
+ if (!masks)
+ aux->masks = &default_aux_masks;
+ else
+ aux->masks = masks;
+
+ aux->reg = reg;
+ aux->rtbl = rtbl;
+ aux->rtbl_cnt = rtbl_cnt;
+ aux->lock = lock;
+ aux->hw.init = &init;
+
+ init.name = aux_name;
+ init.ops = &clk_aux_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &aux->hw);
+ if (IS_ERR_OR_NULL(clk))
+ goto free_aux;
+
+ if (gate_name) {
+ struct clk *tgate_clk;
+
+ tgate_clk = clk_register_gate(NULL, gate_name, aux_name,
+ CLK_SET_RATE_PARENT, reg,
+ aux->masks->enable_bit, 0, lock);
+ if (IS_ERR_OR_NULL(tgate_clk))
+ goto free_aux;
+
+ if (gate_clk)
+ *gate_clk = tgate_clk;
+ }
+
+ return clk;
+
+free_aux:
+ kfree(aux);
+ pr_err("clk register failed\n");
+
+ return NULL;
+}
diff --git a/drivers/clk/spear/clk-frac-synth.c b/drivers/clk/spear/clk-frac-synth.c
new file mode 100644
index 000000000..958aa3ad1
--- /dev/null
+++ b/drivers/clk/spear/clk-frac-synth.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Fractional Synthesizer clock implementation
+ */
+
+#define pr_fmt(fmt) "clk-frac-synth: " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define DIV_FACTOR_MASK 0x1FFFF
+
+/*
+ * DOC: Fractional Synthesizer clock
+ *
+ * Fout from synthesizer can be given from below equation:
+ *
+ * Fout= Fin/2*div (division factor)
+ * div is 17 bits:-
+ * 0-13 (fractional part)
+ * 14-16 (integer part)
+ * div is (16-14 bits).(13-0 bits) (in binary)
+ *
+ * Fout = Fin/(2 * div)
+ * Fout = ((Fin / 10000)/(2 * div)) * 10000
+ * Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000
+ * Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000
+ *
+ * div << 14 simply 17 bit value written at register.
+ * Max error due to scaling down by 10000 is 10 KHz
+ */
+
+#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
+
+static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate,
+ int index)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ struct frac_rate_tbl *rtbl = frac->rtbl;
+
+ prate /= 10000;
+ prate <<= 14;
+ prate /= (2 * rtbl[index].div);
+ prate *= 10000;
+
+ return prate;
+}
+
+static long clk_frac_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ int unused;
+
+ return clk_round_rate_index(hw, drate, *prate, frac_calc_rate,
+ frac->rtbl_cnt, &unused);
+}
+
+static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ unsigned long flags = 0;
+ unsigned int div = 1, val;
+
+ if (frac->lock)
+ spin_lock_irqsave(frac->lock, flags);
+
+ val = readl_relaxed(frac->reg);
+
+ if (frac->lock)
+ spin_unlock_irqrestore(frac->lock, flags);
+
+ div = val & DIV_FACTOR_MASK;
+
+ if (!div)
+ return 0;
+
+ parent_rate = parent_rate / 10000;
+
+ parent_rate = (parent_rate << 14) / (2 * div);
+ return parent_rate * 10000;
+}
+
+/* Configures new clock rate of frac */
+static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_frac *frac = to_clk_frac(hw);
+ struct frac_rate_tbl *rtbl = frac->rtbl;
+ unsigned long flags = 0, val;
+ int i;
+
+ clk_round_rate_index(hw, drate, prate, frac_calc_rate, frac->rtbl_cnt,
+ &i);
+
+ if (frac->lock)
+ spin_lock_irqsave(frac->lock, flags);
+
+ val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK;
+ val |= rtbl[i].div & DIV_FACTOR_MASK;
+ writel_relaxed(val, frac->reg);
+
+ if (frac->lock)
+ spin_unlock_irqrestore(frac->lock, flags);
+
+ return 0;
+}
+
+struct clk_ops clk_frac_ops = {
+ .recalc_rate = clk_frac_recalc_rate,
+ .round_rate = clk_frac_round_rate,
+ .set_rate = clk_frac_set_rate,
+};
+
+struct clk *clk_register_frac(const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg,
+ struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock)
+{
+ struct clk_init_data init;
+ struct clk_frac *frac;
+ struct clk *clk;
+
+ if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
+ pr_err("Invalid arguments passed");
+ return ERR_PTR(-EINVAL);
+ }
+
+ frac = kzalloc(sizeof(*frac), GFP_KERNEL);
+ if (!frac) {
+ pr_err("could not allocate frac clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_frac assignments */
+ frac->reg = reg;
+ frac->rtbl = rtbl;
+ frac->rtbl_cnt = rtbl_cnt;
+ frac->lock = lock;
+ frac->hw.init = &init;
+
+ init.name = name;
+ init.ops = &clk_frac_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &frac->hw);
+ if (!IS_ERR_OR_NULL(clk))
+ return clk;
+
+ pr_err("clk register failed\n");
+ kfree(frac);
+
+ return NULL;
+}
diff --git a/drivers/clk/spear/clk-gpt-synth.c b/drivers/clk/spear/clk-gpt-synth.c
new file mode 100644
index 000000000..1afc18c4e
--- /dev/null
+++ b/drivers/clk/spear/clk-gpt-synth.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * General Purpose Timer Synthesizer clock implementation
+ */
+
+#define pr_fmt(fmt) "clk-gpt-synth: " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define GPT_MSCALE_MASK 0xFFF
+#define GPT_NSCALE_SHIFT 12
+#define GPT_NSCALE_MASK 0xF
+
+/*
+ * DOC: General Purpose Timer Synthesizer clock
+ *
+ * Calculates gpt synth clk rate for different values of mscale and nscale
+ *
+ * Fout= Fin/((2 ^ (N+1)) * (M+1))
+ */
+
+#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw)
+
+static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate,
+ int index)
+{
+ struct clk_gpt *gpt = to_clk_gpt(hw);
+ struct gpt_rate_tbl *rtbl = gpt->rtbl;
+
+ prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1));
+
+ return prate;
+}
+
+static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ struct clk_gpt *gpt = to_clk_gpt(hw);
+ int unused;
+
+ return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate,
+ gpt->rtbl_cnt, &unused);
+}
+
+static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_gpt *gpt = to_clk_gpt(hw);
+ unsigned long flags = 0;
+ unsigned int div = 1, val;
+
+ if (gpt->lock)
+ spin_lock_irqsave(gpt->lock, flags);
+
+ val = readl_relaxed(gpt->reg);
+
+ if (gpt->lock)
+ spin_unlock_irqrestore(gpt->lock, flags);
+
+ div += val & GPT_MSCALE_MASK;
+ div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1);
+
+ if (!div)
+ return 0;
+
+ return parent_rate / div;
+}
+
+/* Configures new clock rate of gpt */
+static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_gpt *gpt = to_clk_gpt(hw);
+ struct gpt_rate_tbl *rtbl = gpt->rtbl;
+ unsigned long flags = 0, val;
+ int i;
+
+ clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt,
+ &i);
+
+ if (gpt->lock)
+ spin_lock_irqsave(gpt->lock, flags);
+
+ val = readl(gpt->reg) & ~GPT_MSCALE_MASK;
+ val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT);
+
+ val |= rtbl[i].mscale & GPT_MSCALE_MASK;
+ val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT;
+
+ writel_relaxed(val, gpt->reg);
+
+ if (gpt->lock)
+ spin_unlock_irqrestore(gpt->lock, flags);
+
+ return 0;
+}
+
+static struct clk_ops clk_gpt_ops = {
+ .recalc_rate = clk_gpt_recalc_rate,
+ .round_rate = clk_gpt_round_rate,
+ .set_rate = clk_gpt_set_rate,
+};
+
+struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned
+ long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8
+ rtbl_cnt, spinlock_t *lock)
+{
+ struct clk_init_data init;
+ struct clk_gpt *gpt;
+ struct clk *clk;
+
+ if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
+ pr_err("Invalid arguments passed");
+ return ERR_PTR(-EINVAL);
+ }
+
+ gpt = kzalloc(sizeof(*gpt), GFP_KERNEL);
+ if (!gpt) {
+ pr_err("could not allocate gpt clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_gpt assignments */
+ gpt->reg = reg;
+ gpt->rtbl = rtbl;
+ gpt->rtbl_cnt = rtbl_cnt;
+ gpt->lock = lock;
+ gpt->hw.init = &init;
+
+ init.name = name;
+ init.ops = &clk_gpt_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &gpt->hw);
+ if (!IS_ERR_OR_NULL(clk))
+ return clk;
+
+ pr_err("clk register failed\n");
+ kfree(gpt);
+
+ return NULL;
+}
diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c
new file mode 100644
index 000000000..1b9b65bca
--- /dev/null
+++ b/drivers/clk/spear/clk-vco-pll.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * VCO-PLL clock implementation
+ */
+
+#define pr_fmt(fmt) "clk-vco-pll: " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+/*
+ * DOC: VCO-PLL clock
+ *
+ * VCO and PLL rate are derived from following equations:
+ *
+ * In normal mode
+ * vco = (2 * M[15:8] * Fin)/N
+ *
+ * In Dithered mode
+ * vco = (2 * M[15:0] * Fin)/(256 * N)
+ *
+ * pll_rate = pll/2^p
+ *
+ * vco and pll are very closely bound to each other, "vco needs to program:
+ * mode, m & n" and "pll needs to program p", both share common enable/disable
+ * logic.
+ *
+ * clk_register_vco_pll() registers instances of both vco & pll.
+ * CLK_SET_RATE_PARENT flag is forced for pll, as it will always pass its
+ * set_rate to vco. A single rate table exists for both the clocks, which
+ * configures m, n and p.
+ */
+
+/* PLL_CTR register masks */
+#define PLL_MODE_NORMAL 0
+#define PLL_MODE_FRACTION 1
+#define PLL_MODE_DITH_DSM 2
+#define PLL_MODE_DITH_SSM 3
+#define PLL_MODE_MASK 3
+#define PLL_MODE_SHIFT 3
+#define PLL_ENABLE 2
+
+#define PLL_LOCK_SHIFT 0
+#define PLL_LOCK_MASK 1
+
+/* PLL FRQ register masks */
+#define PLL_NORM_FDBK_M_MASK 0xFF
+#define PLL_NORM_FDBK_M_SHIFT 24
+#define PLL_DITH_FDBK_M_MASK 0xFFFF
+#define PLL_DITH_FDBK_M_SHIFT 16
+#define PLL_DIV_P_MASK 0x7
+#define PLL_DIV_P_SHIFT 8
+#define PLL_DIV_N_MASK 0xFF
+#define PLL_DIV_N_SHIFT 0
+
+#define to_clk_vco(_hw) container_of(_hw, struct clk_vco, hw)
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+/* Calculates pll clk rate for specific value of mode, m, n and p */
+static unsigned long pll_calc_rate(struct pll_rate_tbl *rtbl,
+ unsigned long prate, int index, unsigned long *pll_rate)
+{
+ unsigned long rate = prate;
+ unsigned int mode;
+
+ mode = rtbl[index].mode ? 256 : 1;
+ rate = (((2 * rate / 10000) * rtbl[index].m) / (mode * rtbl[index].n));
+
+ if (pll_rate)
+ *pll_rate = (rate / (1 << rtbl[index].p)) * 10000;
+
+ return rate * 10000;
+}
+
+static long clk_pll_round_rate_index(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate, int *index)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ unsigned long prev_rate, vco_prev_rate, rate = 0;
+ unsigned long vco_parent_rate =
+ __clk_get_rate(__clk_get_parent(__clk_get_parent(hw->clk)));
+
+ if (!prate) {
+ pr_err("%s: prate is must for pll clk\n", __func__);
+ return -EINVAL;
+ }
+
+ for (*index = 0; *index < pll->vco->rtbl_cnt; (*index)++) {
+ prev_rate = rate;
+ vco_prev_rate = *prate;
+ *prate = pll_calc_rate(pll->vco->rtbl, vco_parent_rate, *index,
+ &rate);
+ if (drate < rate) {
+ /* previous clock was best */
+ if (*index) {
+ rate = prev_rate;
+ *prate = vco_prev_rate;
+ (*index)--;
+ }
+ break;
+ }
+ }
+
+ return rate;
+}
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ int unused;
+
+ return clk_pll_round_rate_index(hw, drate, prate, &unused);
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long
+ parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ unsigned int p;
+
+ if (pll->vco->lock)
+ spin_lock_irqsave(pll->vco->lock, flags);
+
+ p = readl_relaxed(pll->vco->cfg_reg);
+
+ if (pll->vco->lock)
+ spin_unlock_irqrestore(pll->vco->lock, flags);
+
+ p = (p >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK;
+
+ return parent_rate / (1 << p);
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ struct pll_rate_tbl *rtbl = pll->vco->rtbl;
+ unsigned long flags = 0, val;
+ int uninitialized_var(i);
+
+ clk_pll_round_rate_index(hw, drate, NULL, &i);
+
+ if (pll->vco->lock)
+ spin_lock_irqsave(pll->vco->lock, flags);
+
+ val = readl_relaxed(pll->vco->cfg_reg);
+ val &= ~(PLL_DIV_P_MASK << PLL_DIV_P_SHIFT);
+ val |= (rtbl[i].p & PLL_DIV_P_MASK) << PLL_DIV_P_SHIFT;
+ writel_relaxed(val, pll->vco->cfg_reg);
+
+ if (pll->vco->lock)
+ spin_unlock_irqrestore(pll->vco->lock, flags);
+
+ return 0;
+}
+
+static struct clk_ops clk_pll_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_pll_set_rate,
+};
+
+static inline unsigned long vco_calc_rate(struct clk_hw *hw,
+ unsigned long prate, int index)
+{
+ struct clk_vco *vco = to_clk_vco(hw);
+
+ return pll_calc_rate(vco->rtbl, prate, index, NULL);
+}
+
+static long clk_vco_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ struct clk_vco *vco = to_clk_vco(hw);
+ int unused;
+
+ return clk_round_rate_index(hw, drate, *prate, vco_calc_rate,
+ vco->rtbl_cnt, &unused);
+}
+
+static unsigned long clk_vco_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_vco *vco = to_clk_vco(hw);
+ unsigned long flags = 0;
+ unsigned int num = 2, den = 0, val, mode = 0;
+
+ if (vco->lock)
+ spin_lock_irqsave(vco->lock, flags);
+
+ mode = (readl_relaxed(vco->mode_reg) >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
+
+ val = readl_relaxed(vco->cfg_reg);
+
+ if (vco->lock)
+ spin_unlock_irqrestore(vco->lock, flags);
+
+ den = (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK;
+
+ /* calculate numerator & denominator */
+ if (!mode) {
+ /* Normal mode */
+ num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK;
+ } else {
+ /* Dithered mode */
+ num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK;
+ den *= 256;
+ }
+
+ if (!den) {
+ WARN(1, "%s: denominator can't be zero\n", __func__);
+ return 0;
+ }
+
+ return (((parent_rate / 10000) * num) / den) * 10000;
+}
+
+/* Configures new clock rate of vco */
+static int clk_vco_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_vco *vco = to_clk_vco(hw);
+ struct pll_rate_tbl *rtbl = vco->rtbl;
+ unsigned long flags = 0, val;
+ int i;
+
+ clk_round_rate_index(hw, drate, prate, vco_calc_rate, vco->rtbl_cnt,
+ &i);
+
+ if (vco->lock)
+ spin_lock_irqsave(vco->lock, flags);
+
+ val = readl_relaxed(vco->mode_reg);
+ val &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
+ val |= (rtbl[i].mode & PLL_MODE_MASK) << PLL_MODE_SHIFT;
+ writel_relaxed(val, vco->mode_reg);
+
+ val = readl_relaxed(vco->cfg_reg);
+ val &= ~(PLL_DIV_N_MASK << PLL_DIV_N_SHIFT);
+ val |= (rtbl[i].n & PLL_DIV_N_MASK) << PLL_DIV_N_SHIFT;
+
+ val &= ~(PLL_DITH_FDBK_M_MASK << PLL_DITH_FDBK_M_SHIFT);
+ if (rtbl[i].mode)
+ val |= (rtbl[i].m & PLL_DITH_FDBK_M_MASK) <<
+ PLL_DITH_FDBK_M_SHIFT;
+ else
+ val |= (rtbl[i].m & PLL_NORM_FDBK_M_MASK) <<
+ PLL_NORM_FDBK_M_SHIFT;
+
+ writel_relaxed(val, vco->cfg_reg);
+
+ if (vco->lock)
+ spin_unlock_irqrestore(vco->lock, flags);
+
+ return 0;
+}
+
+static struct clk_ops clk_vco_ops = {
+ .recalc_rate = clk_vco_recalc_rate,
+ .round_rate = clk_vco_round_rate,
+ .set_rate = clk_vco_set_rate,
+};
+
+struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name,
+ const char *vco_gate_name, const char *parent_name,
+ unsigned long flags, void __iomem *mode_reg, void __iomem
+ *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt,
+ spinlock_t *lock, struct clk **pll_clk,
+ struct clk **vco_gate_clk)
+{
+ struct clk_vco *vco;
+ struct clk_pll *pll;
+ struct clk *vco_clk, *tpll_clk, *tvco_gate_clk;
+ struct clk_init_data vco_init, pll_init;
+ const char **vco_parent_name;
+
+ if (!vco_name || !pll_name || !parent_name || !mode_reg || !cfg_reg ||
+ !rtbl || !rtbl_cnt) {
+ pr_err("Invalid arguments passed");
+ return ERR_PTR(-EINVAL);
+ }
+
+ vco = kzalloc(sizeof(*vco), GFP_KERNEL);
+ if (!vco) {
+ pr_err("could not allocate vco clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("could not allocate pll clk\n");
+ goto free_vco;
+ }
+
+ /* struct clk_vco assignments */
+ vco->mode_reg = mode_reg;
+ vco->cfg_reg = cfg_reg;
+ vco->rtbl = rtbl;
+ vco->rtbl_cnt = rtbl_cnt;
+ vco->lock = lock;
+ vco->hw.init = &vco_init;
+
+ pll->vco = vco;
+ pll->hw.init = &pll_init;
+
+ if (vco_gate_name) {
+ tvco_gate_clk = clk_register_gate(NULL, vco_gate_name,
+ parent_name, 0, mode_reg, PLL_ENABLE, 0, lock);
+ if (IS_ERR_OR_NULL(tvco_gate_clk))
+ goto free_pll;
+
+ if (vco_gate_clk)
+ *vco_gate_clk = tvco_gate_clk;
+ vco_parent_name = &vco_gate_name;
+ } else {
+ vco_parent_name = &parent_name;
+ }
+
+ vco_init.name = vco_name;
+ vco_init.ops = &clk_vco_ops;
+ vco_init.flags = flags;
+ vco_init.parent_names = vco_parent_name;
+ vco_init.num_parents = 1;
+
+ pll_init.name = pll_name;
+ pll_init.ops = &clk_pll_ops;
+ pll_init.flags = CLK_SET_RATE_PARENT;
+ pll_init.parent_names = &vco_name;
+ pll_init.num_parents = 1;
+
+ vco_clk = clk_register(NULL, &vco->hw);
+ if (IS_ERR_OR_NULL(vco_clk))
+ goto free_pll;
+
+ tpll_clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR_OR_NULL(tpll_clk))
+ goto free_pll;
+
+ if (pll_clk)
+ *pll_clk = tpll_clk;
+
+ return vco_clk;
+
+free_pll:
+ kfree(pll);
+free_vco:
+ kfree(vco);
+
+ pr_err("Failed to register vco pll clock\n");
+
+ return ERR_PTR(-ENOMEM);
+}
diff --git a/drivers/clk/spear/clk.c b/drivers/clk/spear/clk.c
new file mode 100644
index 000000000..628b6d5ed
--- /dev/null
+++ b/drivers/clk/spear/clk.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * SPEAr clk - Common routines
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/types.h>
+#include "clk.h"
+
+long clk_round_rate_index(struct clk_hw *hw, unsigned long drate,
+ unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt,
+ int *index)
+{
+ unsigned long prev_rate, rate = 0;
+
+ for (*index = 0; *index < rtbl_cnt; (*index)++) {
+ prev_rate = rate;
+ rate = calc_rate(hw, parent_rate, *index);
+ if (drate < rate) {
+ /* previous clock was best */
+ if (*index) {
+ rate = prev_rate;
+ (*index)--;
+ }
+ break;
+ }
+ }
+
+ if ((*index) == rtbl_cnt)
+ (*index)--;
+
+ return rate;
+}
diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h
new file mode 100644
index 000000000..931737677
--- /dev/null
+++ b/drivers/clk/spear/clk.h
@@ -0,0 +1,134 @@
+/*
+ * Clock framework definitions for SPEAr platform
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __SPEAR_CLK_H
+#define __SPEAR_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+/* Auxiliary Synth clk */
+/* Default masks */
+#define AUX_EQ_SEL_SHIFT 30
+#define AUX_EQ_SEL_MASK 1
+#define AUX_EQ1_SEL 0
+#define AUX_EQ2_SEL 1
+#define AUX_XSCALE_SHIFT 16
+#define AUX_XSCALE_MASK 0xFFF
+#define AUX_YSCALE_SHIFT 0
+#define AUX_YSCALE_MASK 0xFFF
+#define AUX_SYNT_ENB 31
+
+struct aux_clk_masks {
+ u32 eq_sel_mask;
+ u32 eq_sel_shift;
+ u32 eq1_mask;
+ u32 eq2_mask;
+ u32 xscale_sel_mask;
+ u32 xscale_sel_shift;
+ u32 yscale_sel_mask;
+ u32 yscale_sel_shift;
+ u32 enable_bit;
+};
+
+struct aux_rate_tbl {
+ u16 xscale;
+ u16 yscale;
+ u8 eq;
+};
+
+struct clk_aux {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct aux_clk_masks *masks;
+ struct aux_rate_tbl *rtbl;
+ u8 rtbl_cnt;
+ spinlock_t *lock;
+};
+
+/* Fractional Synth clk */
+struct frac_rate_tbl {
+ u32 div;
+};
+
+struct clk_frac {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct frac_rate_tbl *rtbl;
+ u8 rtbl_cnt;
+ spinlock_t *lock;
+};
+
+/* GPT clk */
+struct gpt_rate_tbl {
+ u16 mscale;
+ u16 nscale;
+};
+
+struct clk_gpt {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct gpt_rate_tbl *rtbl;
+ u8 rtbl_cnt;
+ spinlock_t *lock;
+};
+
+/* VCO-PLL clk */
+struct pll_rate_tbl {
+ u8 mode;
+ u16 m;
+ u8 n;
+ u8 p;
+};
+
+struct clk_vco {
+ struct clk_hw hw;
+ void __iomem *mode_reg;
+ void __iomem *cfg_reg;
+ struct pll_rate_tbl *rtbl;
+ u8 rtbl_cnt;
+ spinlock_t *lock;
+};
+
+struct clk_pll {
+ struct clk_hw hw;
+ struct clk_vco *vco;
+ const char *parent[1];
+ spinlock_t *lock;
+};
+
+typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate,
+ int index);
+
+/* clk register routines */
+struct clk *clk_register_aux(const char *aux_name, const char *gate_name,
+ const char *parent_name, unsigned long flags, void __iomem *reg,
+ struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl,
+ u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk);
+struct clk *clk_register_frac(const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg,
+ struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock);
+struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned
+ long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8
+ rtbl_cnt, spinlock_t *lock);
+struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name,
+ const char *vco_gate_name, const char *parent_name,
+ unsigned long flags, void __iomem *mode_reg, void __iomem
+ *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt,
+ spinlock_t *lock, struct clk **pll_clk,
+ struct clk **vco_gate_clk);
+
+long clk_round_rate_index(struct clk_hw *hw, unsigned long drate,
+ unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt,
+ int *index);
+
+#endif /* __SPEAR_CLK_H */
diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c
new file mode 100644
index 000000000..aedbbe12f
--- /dev/null
+++ b/drivers/clk/spear/spear1310_clock.c
@@ -0,0 +1,1121 @@
+/*
+ * arch/arm/mach-spear13xx/spear1310_clock.c
+ *
+ * SPEAr1310 machine clock framework source file
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock_types.h>
+#include "clk.h"
+
+/* PLL related registers and bit values */
+#define SPEAR1310_PLL_CFG (misc_base + 0x210)
+ /* PLL_CFG bit values */
+ #define SPEAR1310_CLCD_SYNT_CLK_MASK 1
+ #define SPEAR1310_CLCD_SYNT_CLK_SHIFT 31
+ #define SPEAR1310_RAS_SYNT2_3_CLK_MASK 2
+ #define SPEAR1310_RAS_SYNT2_3_CLK_SHIFT 29
+ #define SPEAR1310_RAS_SYNT_CLK_MASK 2
+ #define SPEAR1310_RAS_SYNT0_1_CLK_SHIFT 27
+ #define SPEAR1310_PLL_CLK_MASK 2
+ #define SPEAR1310_PLL3_CLK_SHIFT 24
+ #define SPEAR1310_PLL2_CLK_SHIFT 22
+ #define SPEAR1310_PLL1_CLK_SHIFT 20
+
+#define SPEAR1310_PLL1_CTR (misc_base + 0x214)
+#define SPEAR1310_PLL1_FRQ (misc_base + 0x218)
+#define SPEAR1310_PLL2_CTR (misc_base + 0x220)
+#define SPEAR1310_PLL2_FRQ (misc_base + 0x224)
+#define SPEAR1310_PLL3_CTR (misc_base + 0x22C)
+#define SPEAR1310_PLL3_FRQ (misc_base + 0x230)
+#define SPEAR1310_PLL4_CTR (misc_base + 0x238)
+#define SPEAR1310_PLL4_FRQ (misc_base + 0x23C)
+#define SPEAR1310_PERIP_CLK_CFG (misc_base + 0x244)
+ /* PERIP_CLK_CFG bit values */
+ #define SPEAR1310_GPT_OSC24_VAL 0
+ #define SPEAR1310_GPT_APB_VAL 1
+ #define SPEAR1310_GPT_CLK_MASK 1
+ #define SPEAR1310_GPT3_CLK_SHIFT 11
+ #define SPEAR1310_GPT2_CLK_SHIFT 10
+ #define SPEAR1310_GPT1_CLK_SHIFT 9
+ #define SPEAR1310_GPT0_CLK_SHIFT 8
+ #define SPEAR1310_UART_CLK_PLL5_VAL 0
+ #define SPEAR1310_UART_CLK_OSC24_VAL 1
+ #define SPEAR1310_UART_CLK_SYNT_VAL 2
+ #define SPEAR1310_UART_CLK_MASK 2
+ #define SPEAR1310_UART_CLK_SHIFT 4
+
+ #define SPEAR1310_AUX_CLK_PLL5_VAL 0
+ #define SPEAR1310_AUX_CLK_SYNT_VAL 1
+ #define SPEAR1310_CLCD_CLK_MASK 2
+ #define SPEAR1310_CLCD_CLK_SHIFT 2
+ #define SPEAR1310_C3_CLK_MASK 1
+ #define SPEAR1310_C3_CLK_SHIFT 1
+
+#define SPEAR1310_GMAC_CLK_CFG (misc_base + 0x248)
+ #define SPEAR1310_GMAC_PHY_IF_SEL_MASK 3
+ #define SPEAR1310_GMAC_PHY_IF_SEL_SHIFT 4
+ #define SPEAR1310_GMAC_PHY_CLK_MASK 1
+ #define SPEAR1310_GMAC_PHY_CLK_SHIFT 3
+ #define SPEAR1310_GMAC_PHY_INPUT_CLK_MASK 2
+ #define SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT 1
+
+#define SPEAR1310_I2S_CLK_CFG (misc_base + 0x24C)
+ /* I2S_CLK_CFG register mask */
+ #define SPEAR1310_I2S_SCLK_X_MASK 0x1F
+ #define SPEAR1310_I2S_SCLK_X_SHIFT 27
+ #define SPEAR1310_I2S_SCLK_Y_MASK 0x1F
+ #define SPEAR1310_I2S_SCLK_Y_SHIFT 22
+ #define SPEAR1310_I2S_SCLK_EQ_SEL_SHIFT 21
+ #define SPEAR1310_I2S_SCLK_SYNTH_ENB 20
+ #define SPEAR1310_I2S_PRS1_CLK_X_MASK 0xFF
+ #define SPEAR1310_I2S_PRS1_CLK_X_SHIFT 12
+ #define SPEAR1310_I2S_PRS1_CLK_Y_MASK 0xFF
+ #define SPEAR1310_I2S_PRS1_CLK_Y_SHIFT 4
+ #define SPEAR1310_I2S_PRS1_EQ_SEL_SHIFT 3
+ #define SPEAR1310_I2S_REF_SEL_MASK 1
+ #define SPEAR1310_I2S_REF_SHIFT 2
+ #define SPEAR1310_I2S_SRC_CLK_MASK 2
+ #define SPEAR1310_I2S_SRC_CLK_SHIFT 0
+
+#define SPEAR1310_C3_CLK_SYNT (misc_base + 0x250)
+#define SPEAR1310_UART_CLK_SYNT (misc_base + 0x254)
+#define SPEAR1310_GMAC_CLK_SYNT (misc_base + 0x258)
+#define SPEAR1310_SDHCI_CLK_SYNT (misc_base + 0x25C)
+#define SPEAR1310_CFXD_CLK_SYNT (misc_base + 0x260)
+#define SPEAR1310_ADC_CLK_SYNT (misc_base + 0x264)
+#define SPEAR1310_AMBA_CLK_SYNT (misc_base + 0x268)
+#define SPEAR1310_CLCD_CLK_SYNT (misc_base + 0x270)
+#define SPEAR1310_RAS_CLK_SYNT0 (misc_base + 0x280)
+#define SPEAR1310_RAS_CLK_SYNT1 (misc_base + 0x288)
+#define SPEAR1310_RAS_CLK_SYNT2 (misc_base + 0x290)
+#define SPEAR1310_RAS_CLK_SYNT3 (misc_base + 0x298)
+ /* Check Fractional synthesizer reg masks */
+
+#define SPEAR1310_PERIP1_CLK_ENB (misc_base + 0x300)
+ /* PERIP1_CLK_ENB register masks */
+ #define SPEAR1310_RTC_CLK_ENB 31
+ #define SPEAR1310_ADC_CLK_ENB 30
+ #define SPEAR1310_C3_CLK_ENB 29
+ #define SPEAR1310_JPEG_CLK_ENB 28
+ #define SPEAR1310_CLCD_CLK_ENB 27
+ #define SPEAR1310_DMA_CLK_ENB 25
+ #define SPEAR1310_GPIO1_CLK_ENB 24
+ #define SPEAR1310_GPIO0_CLK_ENB 23
+ #define SPEAR1310_GPT1_CLK_ENB 22
+ #define SPEAR1310_GPT0_CLK_ENB 21
+ #define SPEAR1310_I2S0_CLK_ENB 20
+ #define SPEAR1310_I2S1_CLK_ENB 19
+ #define SPEAR1310_I2C0_CLK_ENB 18
+ #define SPEAR1310_SSP_CLK_ENB 17
+ #define SPEAR1310_UART_CLK_ENB 15
+ #define SPEAR1310_PCIE_SATA_2_CLK_ENB 14
+ #define SPEAR1310_PCIE_SATA_1_CLK_ENB 13
+ #define SPEAR1310_PCIE_SATA_0_CLK_ENB 12
+ #define SPEAR1310_UOC_CLK_ENB 11
+ #define SPEAR1310_UHC1_CLK_ENB 10
+ #define SPEAR1310_UHC0_CLK_ENB 9
+ #define SPEAR1310_GMAC_CLK_ENB 8
+ #define SPEAR1310_CFXD_CLK_ENB 7
+ #define SPEAR1310_SDHCI_CLK_ENB 6
+ #define SPEAR1310_SMI_CLK_ENB 5
+ #define SPEAR1310_FSMC_CLK_ENB 4
+ #define SPEAR1310_SYSRAM0_CLK_ENB 3
+ #define SPEAR1310_SYSRAM1_CLK_ENB 2
+ #define SPEAR1310_SYSROM_CLK_ENB 1
+ #define SPEAR1310_BUS_CLK_ENB 0
+
+#define SPEAR1310_PERIP2_CLK_ENB (misc_base + 0x304)
+ /* PERIP2_CLK_ENB register masks */
+ #define SPEAR1310_THSENS_CLK_ENB 8
+ #define SPEAR1310_I2S_REF_PAD_CLK_ENB 7
+ #define SPEAR1310_ACP_CLK_ENB 6
+ #define SPEAR1310_GPT3_CLK_ENB 5
+ #define SPEAR1310_GPT2_CLK_ENB 4
+ #define SPEAR1310_KBD_CLK_ENB 3
+ #define SPEAR1310_CPU_DBG_CLK_ENB 2
+ #define SPEAR1310_DDR_CORE_CLK_ENB 1
+ #define SPEAR1310_DDR_CTRL_CLK_ENB 0
+
+#define SPEAR1310_RAS_CLK_ENB (misc_base + 0x310)
+ /* RAS_CLK_ENB register masks */
+ #define SPEAR1310_SYNT3_CLK_ENB 17
+ #define SPEAR1310_SYNT2_CLK_ENB 16
+ #define SPEAR1310_SYNT1_CLK_ENB 15
+ #define SPEAR1310_SYNT0_CLK_ENB 14
+ #define SPEAR1310_PCLK3_CLK_ENB 13
+ #define SPEAR1310_PCLK2_CLK_ENB 12
+ #define SPEAR1310_PCLK1_CLK_ENB 11
+ #define SPEAR1310_PCLK0_CLK_ENB 10
+ #define SPEAR1310_PLL3_CLK_ENB 9
+ #define SPEAR1310_PLL2_CLK_ENB 8
+ #define SPEAR1310_C125M_PAD_CLK_ENB 7
+ #define SPEAR1310_C30M_CLK_ENB 6
+ #define SPEAR1310_C48M_CLK_ENB 5
+ #define SPEAR1310_OSC_25M_CLK_ENB 4
+ #define SPEAR1310_OSC_32K_CLK_ENB 3
+ #define SPEAR1310_OSC_24M_CLK_ENB 2
+ #define SPEAR1310_PCLK_CLK_ENB 1
+ #define SPEAR1310_ACLK_CLK_ENB 0
+
+/* RAS Area Control Register */
+#define SPEAR1310_RAS_CTRL_REG0 (ras_base + 0x000)
+ #define SPEAR1310_SSP1_CLK_MASK 3
+ #define SPEAR1310_SSP1_CLK_SHIFT 26
+ #define SPEAR1310_TDM_CLK_MASK 1
+ #define SPEAR1310_TDM2_CLK_SHIFT 24
+ #define SPEAR1310_TDM1_CLK_SHIFT 23
+ #define SPEAR1310_I2C_CLK_MASK 1
+ #define SPEAR1310_I2C7_CLK_SHIFT 22
+ #define SPEAR1310_I2C6_CLK_SHIFT 21
+ #define SPEAR1310_I2C5_CLK_SHIFT 20
+ #define SPEAR1310_I2C4_CLK_SHIFT 19
+ #define SPEAR1310_I2C3_CLK_SHIFT 18
+ #define SPEAR1310_I2C2_CLK_SHIFT 17
+ #define SPEAR1310_I2C1_CLK_SHIFT 16
+ #define SPEAR1310_GPT64_CLK_MASK 1
+ #define SPEAR1310_GPT64_CLK_SHIFT 15
+ #define SPEAR1310_RAS_UART_CLK_MASK 1
+ #define SPEAR1310_UART5_CLK_SHIFT 14
+ #define SPEAR1310_UART4_CLK_SHIFT 13
+ #define SPEAR1310_UART3_CLK_SHIFT 12
+ #define SPEAR1310_UART2_CLK_SHIFT 11
+ #define SPEAR1310_UART1_CLK_SHIFT 10
+ #define SPEAR1310_PCI_CLK_MASK 1
+ #define SPEAR1310_PCI_CLK_SHIFT 0
+
+#define SPEAR1310_RAS_CTRL_REG1 (ras_base + 0x004)
+ #define SPEAR1310_PHY_CLK_MASK 0x3
+ #define SPEAR1310_RMII_PHY_CLK_SHIFT 0
+ #define SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT 2
+
+#define SPEAR1310_RAS_SW_CLK_CTRL (ras_base + 0x0148)
+ #define SPEAR1310_CAN1_CLK_ENB 25
+ #define SPEAR1310_CAN0_CLK_ENB 24
+ #define SPEAR1310_GPT64_CLK_ENB 23
+ #define SPEAR1310_SSP1_CLK_ENB 22
+ #define SPEAR1310_I2C7_CLK_ENB 21
+ #define SPEAR1310_I2C6_CLK_ENB 20
+ #define SPEAR1310_I2C5_CLK_ENB 19
+ #define SPEAR1310_I2C4_CLK_ENB 18
+ #define SPEAR1310_I2C3_CLK_ENB 17
+ #define SPEAR1310_I2C2_CLK_ENB 16
+ #define SPEAR1310_I2C1_CLK_ENB 15
+ #define SPEAR1310_UART5_CLK_ENB 14
+ #define SPEAR1310_UART4_CLK_ENB 13
+ #define SPEAR1310_UART3_CLK_ENB 12
+ #define SPEAR1310_UART2_CLK_ENB 11
+ #define SPEAR1310_UART1_CLK_ENB 10
+ #define SPEAR1310_RS485_1_CLK_ENB 9
+ #define SPEAR1310_RS485_0_CLK_ENB 8
+ #define SPEAR1310_TDM2_CLK_ENB 7
+ #define SPEAR1310_TDM1_CLK_ENB 6
+ #define SPEAR1310_PCI_CLK_ENB 5
+ #define SPEAR1310_GMII_CLK_ENB 4
+ #define SPEAR1310_MII2_CLK_ENB 3
+ #define SPEAR1310_MII1_CLK_ENB 2
+ #define SPEAR1310_MII0_CLK_ENB 1
+ #define SPEAR1310_ESRAM_CLK_ENB 0
+
+static DEFINE_SPINLOCK(_lock);
+
+/* pll rate configuration table, in ascending order of rates */
+static struct pll_rate_tbl pll_rtbl[] = {
+ /* PCLK 24MHz */
+ {.mode = 0, .m = 0x83, .n = 0x04, .p = 0x5}, /* vco 1572, pll 49.125 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x3}, /* vco 1000, pll 125 MHz */
+ {.mode = 0, .m = 0x64, .n = 0x06, .p = 0x1}, /* vco 800, pll 400 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x1}, /* vco 1000, pll 500 MHz */
+ {.mode = 0, .m = 0xA6, .n = 0x06, .p = 0x1}, /* vco 1328, pll 664 MHz */
+ {.mode = 0, .m = 0xC8, .n = 0x06, .p = 0x1}, /* vco 1600, pll 800 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x0}, /* vco 1, pll 1 GHz */
+};
+
+/* vco-pll4 rate configuration table, in ascending order of rates */
+static struct pll_rate_tbl pll4_rtbl[] = {
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x2}, /* vco 1000, pll 250 MHz */
+ {.mode = 0, .m = 0xA6, .n = 0x06, .p = 0x2}, /* vco 1328, pll 332 MHz */
+ {.mode = 0, .m = 0xC8, .n = 0x06, .p = 0x2}, /* vco 1600, pll 400 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x0}, /* vco 1, pll 1 GHz */
+};
+
+/* aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl aux_rtbl[] = {
+ /* For VCO1div2 = 500 MHz */
+ {.xscale = 10, .yscale = 204, .eq = 0}, /* 12.29 MHz */
+ {.xscale = 4, .yscale = 21, .eq = 0}, /* 48 MHz */
+ {.xscale = 2, .yscale = 6, .eq = 0}, /* 83 MHz */
+ {.xscale = 2, .yscale = 4, .eq = 0}, /* 125 MHz */
+ {.xscale = 1, .yscale = 3, .eq = 1}, /* 166 MHz */
+ {.xscale = 1, .yscale = 2, .eq = 1}, /* 250 MHz */
+};
+
+/* gmac rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl gmac_rtbl[] = {
+ /* For gmac phy input clk */
+ {.xscale = 2, .yscale = 6, .eq = 0}, /* divided by 6 */
+ {.xscale = 2, .yscale = 4, .eq = 0}, /* divided by 4 */
+ {.xscale = 1, .yscale = 3, .eq = 1}, /* divided by 3 */
+ {.xscale = 1, .yscale = 2, .eq = 1}, /* divided by 2 */
+};
+
+/* clcd rate configuration table, in ascending order of rates */
+static struct frac_rate_tbl clcd_rtbl[] = {
+ {.div = 0x14000}, /* 25 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x1284B}, /* 27 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x0D8D3}, /* 58 Mhz , for vco1div4 = 393 MHz */
+ {.div = 0x0B72C}, /* 58 Mhz , for vco1div4 = 332 MHz */
+ {.div = 0x089EE}, /* 58 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x06f1C}, /* 72 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x06E58}, /* 58 Mhz , for vco1div4 = 200 MHz */
+ {.div = 0x06c1B}, /* 74 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x04A12}, /* 108 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x0378E}, /* 144 Mhz , for vc01div4 = 250 MHz*/
+};
+
+/* i2s prescaler1 masks */
+static struct aux_clk_masks i2s_prs1_masks = {
+ .eq_sel_mask = AUX_EQ_SEL_MASK,
+ .eq_sel_shift = SPEAR1310_I2S_PRS1_EQ_SEL_SHIFT,
+ .eq1_mask = AUX_EQ1_SEL,
+ .eq2_mask = AUX_EQ2_SEL,
+ .xscale_sel_mask = SPEAR1310_I2S_PRS1_CLK_X_MASK,
+ .xscale_sel_shift = SPEAR1310_I2S_PRS1_CLK_X_SHIFT,
+ .yscale_sel_mask = SPEAR1310_I2S_PRS1_CLK_Y_MASK,
+ .yscale_sel_shift = SPEAR1310_I2S_PRS1_CLK_Y_SHIFT,
+};
+
+/* i2s sclk (bit clock) syynthesizers masks */
+static struct aux_clk_masks i2s_sclk_masks = {
+ .eq_sel_mask = AUX_EQ_SEL_MASK,
+ .eq_sel_shift = SPEAR1310_I2S_SCLK_EQ_SEL_SHIFT,
+ .eq1_mask = AUX_EQ1_SEL,
+ .eq2_mask = AUX_EQ2_SEL,
+ .xscale_sel_mask = SPEAR1310_I2S_SCLK_X_MASK,
+ .xscale_sel_shift = SPEAR1310_I2S_SCLK_X_SHIFT,
+ .yscale_sel_mask = SPEAR1310_I2S_SCLK_Y_MASK,
+ .yscale_sel_shift = SPEAR1310_I2S_SCLK_Y_SHIFT,
+ .enable_bit = SPEAR1310_I2S_SCLK_SYNTH_ENB,
+};
+
+/* i2s prs1 aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl i2s_prs1_rtbl[] = {
+ /* For parent clk = 49.152 MHz */
+ {.xscale = 1, .yscale = 12, .eq = 0}, /* 2.048 MHz, smp freq = 8Khz */
+ {.xscale = 11, .yscale = 96, .eq = 0}, /* 2.816 MHz, smp freq = 11Khz */
+ {.xscale = 1, .yscale = 6, .eq = 0}, /* 4.096 MHz, smp freq = 16Khz */
+ {.xscale = 11, .yscale = 48, .eq = 0}, /* 5.632 MHz, smp freq = 22Khz */
+
+ /*
+ * with parent clk = 49.152, freq gen is 8.192 MHz, smp freq = 32Khz
+ * with parent clk = 12.288, freq gen is 2.048 MHz, smp freq = 8Khz
+ */
+ {.xscale = 1, .yscale = 3, .eq = 0},
+
+ /* For parent clk = 49.152 MHz */
+ {.xscale = 17, .yscale = 37, .eq = 0}, /* 11.289 MHz, smp freq = 44Khz*/
+
+ {.xscale = 1, .yscale = 2, .eq = 0}, /* 12.288 MHz */
+};
+
+/* i2s sclk aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl i2s_sclk_rtbl[] = {
+ /* For i2s_ref_clk = 12.288MHz */
+ {.xscale = 1, .yscale = 4, .eq = 0}, /* 1.53 MHz */
+ {.xscale = 1, .yscale = 2, .eq = 0}, /* 3.07 Mhz */
+};
+
+/* adc rate configuration table, in ascending order of rates */
+/* possible adc range is 2.5 MHz to 20 MHz. */
+static struct aux_rate_tbl adc_rtbl[] = {
+ /* For ahb = 166.67 MHz */
+ {.xscale = 1, .yscale = 31, .eq = 0}, /* 2.68 MHz */
+ {.xscale = 2, .yscale = 21, .eq = 0}, /* 7.94 MHz */
+ {.xscale = 4, .yscale = 21, .eq = 0}, /* 15.87 MHz */
+ {.xscale = 10, .yscale = 42, .eq = 0}, /* 19.84 MHz */
+};
+
+/* General synth rate configuration table, in ascending order of rates */
+static struct frac_rate_tbl gen_rtbl[] = {
+ /* For vco1div4 = 250 MHz */
+ {.div = 0x14000}, /* 25 MHz */
+ {.div = 0x0A000}, /* 50 MHz */
+ {.div = 0x05000}, /* 100 MHz */
+ {.div = 0x02000}, /* 250 MHz */
+};
+
+/* clock parents */
+static const char *vco_parents[] = { "osc_24m_clk", "osc_25m_clk", };
+static const char *gpt_parents[] = { "osc_24m_clk", "apb_clk", };
+static const char *uart0_parents[] = { "pll5_clk", "uart_syn_gclk", };
+static const char *c3_parents[] = { "pll5_clk", "c3_syn_gclk", };
+static const char *gmac_phy_input_parents[] = { "gmii_pad_clk", "pll2_clk",
+ "osc_25m_clk", };
+static const char *gmac_phy_parents[] = { "phy_input_mclk", "phy_syn_gclk", };
+static const char *clcd_synth_parents[] = { "vco1div4_clk", "pll2_clk", };
+static const char *clcd_pixel_parents[] = { "pll5_clk", "clcd_syn_clk", };
+static const char *i2s_src_parents[] = { "vco1div2_clk", "none", "pll3_clk",
+ "i2s_src_pad_clk", };
+static const char *i2s_ref_parents[] = { "i2s_src_mclk", "i2s_prs1_clk", };
+static const char *gen_synth0_1_parents[] = { "vco1div4_clk", "vco3div2_clk",
+ "pll3_clk", };
+static const char *gen_synth2_3_parents[] = { "vco1div4_clk", "vco3div2_clk",
+ "pll2_clk", };
+static const char *rmii_phy_parents[] = { "ras_tx50_clk", "none",
+ "ras_pll2_clk", "ras_syn0_clk", };
+static const char *smii_rgmii_phy_parents[] = { "none", "ras_tx125_clk",
+ "ras_pll2_clk", "ras_syn0_clk", };
+static const char *uart_parents[] = { "ras_apb_clk", "gen_syn3_clk", };
+static const char *i2c_parents[] = { "ras_apb_clk", "gen_syn1_clk", };
+static const char *ssp1_parents[] = { "ras_apb_clk", "gen_syn1_clk",
+ "ras_plclk0_clk", };
+static const char *pci_parents[] = { "ras_pll3_clk", "gen_syn2_clk", };
+static const char *tdm_parents[] = { "ras_pll3_clk", "gen_syn1_clk", };
+
+void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
+{
+ struct clk *clk, *clk1;
+
+ clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT,
+ 32000);
+ clk_register_clkdev(clk, "osc_32k_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "osc_24m_clk", NULL, CLK_IS_ROOT,
+ 24000000);
+ clk_register_clkdev(clk, "osc_24m_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "osc_25m_clk", NULL, CLK_IS_ROOT,
+ 25000000);
+ clk_register_clkdev(clk, "osc_25m_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "gmii_pad_clk", NULL, CLK_IS_ROOT,
+ 125000000);
+ clk_register_clkdev(clk, "gmii_pad_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "i2s_src_pad_clk", NULL,
+ CLK_IS_ROOT, 12288000);
+ clk_register_clkdev(clk, "i2s_src_pad_clk", NULL);
+
+ /* clock derived from 32 KHz osc clk */
+ clk = clk_register_gate(NULL, "rtc-spear", "osc_32k_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_RTC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0580000.rtc");
+
+ /* clock derived from 24 or 25 MHz osc clk */
+ /* vco-pll */
+ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents,
+ ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG,
+ SPEAR1310_PLL1_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "vco1_mclk", NULL);
+ clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk",
+ 0, SPEAR1310_PLL1_CTR, SPEAR1310_PLL1_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco1_clk", NULL);
+ clk_register_clkdev(clk1, "pll1_clk", NULL);
+
+ clk = clk_register_mux(NULL, "vco2_mclk", vco_parents,
+ ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG,
+ SPEAR1310_PLL2_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "vco2_mclk", NULL);
+ clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk",
+ 0, SPEAR1310_PLL2_CTR, SPEAR1310_PLL2_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco2_clk", NULL);
+ clk_register_clkdev(clk1, "pll2_clk", NULL);
+
+ clk = clk_register_mux(NULL, "vco3_mclk", vco_parents,
+ ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG,
+ SPEAR1310_PLL3_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "vco3_mclk", NULL);
+ clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk",
+ 0, SPEAR1310_PLL3_CTR, SPEAR1310_PLL3_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco3_clk", NULL);
+ clk_register_clkdev(clk1, "pll3_clk", NULL);
+
+ clk = clk_register_vco_pll("vco4_clk", "pll4_clk", NULL, "osc_24m_clk",
+ 0, SPEAR1310_PLL4_CTR, SPEAR1310_PLL4_FRQ, pll4_rtbl,
+ ARRAY_SIZE(pll4_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco4_clk", NULL);
+ clk_register_clkdev(clk1, "pll4_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll5_clk", "osc_24m_clk", 0,
+ 48000000);
+ clk_register_clkdev(clk, "pll5_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll6_clk", "osc_25m_clk", 0,
+ 25000000);
+ clk_register_clkdev(clk, "pll6_clk", NULL);
+
+ /* vco div n clocks */
+ clk = clk_register_fixed_factor(NULL, "vco1div2_clk", "vco1_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "vco1div2_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vco1div4_clk", "vco1_clk", 0, 1,
+ 4);
+ clk_register_clkdev(clk, "vco1div4_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vco2div2_clk", "vco2_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "vco2div2_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vco3div2_clk", "vco3_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "vco3div2_clk", NULL);
+
+ /* peripherals */
+ clk_register_fixed_factor(NULL, "thermal_clk", "osc_24m_clk", 0, 1,
+ 128);
+ clk = clk_register_gate(NULL, "thermal_gclk", "thermal_clk", 0,
+ SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_THSENS_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "spear_thermal");
+
+ /* clock derived from pll4 clk */
+ clk = clk_register_fixed_factor(NULL, "ddr_clk", "pll4_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "ddr_clk", NULL);
+
+ /* clock derived from pll1 clk */
+ clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "cpu_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "wdt_clk", "cpu_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, NULL, "ec800620.wdt");
+
+ clk = clk_register_fixed_factor(NULL, "smp_twd_clk", "cpu_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, NULL, "smp_twd");
+
+ clk = clk_register_fixed_factor(NULL, "ahb_clk", "pll1_clk", 0, 1,
+ 6);
+ clk_register_clkdev(clk, "ahb_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "apb_clk", "pll1_clk", 0, 1,
+ 12);
+ clk_register_clkdev(clk, "apb_clk", NULL);
+
+ /* gpt clocks */
+ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
+ SPEAR1310_GPT0_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt0_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt0");
+
+ clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
+ SPEAR1310_GPT1_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt1_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt1");
+
+ clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
+ SPEAR1310_GPT2_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt2_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0,
+ SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt2");
+
+ clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
+ SPEAR1310_GPT3_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt3_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0,
+ SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt3");
+
+ /* others */
+ clk = clk_register_aux("uart_syn_clk", "uart_syn_gclk", "vco1div2_clk",
+ 0, SPEAR1310_UART_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "uart_syn_clk", NULL);
+ clk_register_clkdev(clk1, "uart_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents,
+ ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_UART_CLK_SHIFT,
+ SPEAR1310_UART_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "uart0_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart0_clk", "uart0_mclk",
+ CLK_SET_RATE_PARENT, SPEAR1310_PERIP1_CLK_ENB,
+ SPEAR1310_UART_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e0000000.serial");
+
+ clk = clk_register_aux("sdhci_syn_clk", "sdhci_syn_gclk",
+ "vco1div2_clk", 0, SPEAR1310_SDHCI_CLK_SYNT, NULL,
+ aux_rtbl, ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "sdhci_syn_clk", NULL);
+ clk_register_clkdev(clk1, "sdhci_syn_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "sdhci_clk", "sdhci_syn_gclk",
+ CLK_SET_RATE_PARENT, SPEAR1310_PERIP1_CLK_ENB,
+ SPEAR1310_SDHCI_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "b3000000.sdhci");
+
+ clk = clk_register_aux("cfxd_syn_clk", "cfxd_syn_gclk", "vco1div2_clk",
+ 0, SPEAR1310_CFXD_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "cfxd_syn_clk", NULL);
+ clk_register_clkdev(clk1, "cfxd_syn_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "cfxd_clk", "cfxd_syn_gclk",
+ CLK_SET_RATE_PARENT, SPEAR1310_PERIP1_CLK_ENB,
+ SPEAR1310_CFXD_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "b2800000.cf");
+ clk_register_clkdev(clk, NULL, "arasan_xd");
+
+ clk = clk_register_aux("c3_syn_clk", "c3_syn_gclk", "vco1div2_clk",
+ 0, SPEAR1310_C3_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "c3_syn_clk", NULL);
+ clk_register_clkdev(clk1, "c3_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "c3_mclk", c3_parents,
+ ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_C3_CLK_SHIFT,
+ SPEAR1310_C3_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "c3_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "c3_clk", "c3_mclk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_C3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "c3");
+
+ /* gmac */
+ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents,
+ ARRAY_SIZE(gmac_phy_input_parents), 0,
+ SPEAR1310_GMAC_CLK_CFG,
+ SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT,
+ SPEAR1310_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "phy_input_mclk", NULL);
+
+ clk = clk_register_aux("phy_syn_clk", "phy_syn_gclk", "phy_input_mclk",
+ 0, SPEAR1310_GMAC_CLK_SYNT, NULL, gmac_rtbl,
+ ARRAY_SIZE(gmac_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "phy_syn_clk", NULL);
+ clk_register_clkdev(clk1, "phy_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents,
+ ARRAY_SIZE(gmac_phy_parents), 0,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GMAC_PHY_CLK_SHIFT,
+ SPEAR1310_GMAC_PHY_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "stmmacphy.0", NULL);
+
+ /* clcd */
+ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents,
+ ARRAY_SIZE(clcd_synth_parents), 0,
+ SPEAR1310_CLCD_CLK_SYNT, SPEAR1310_CLCD_SYNT_CLK_SHIFT,
+ SPEAR1310_CLCD_SYNT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "clcd_syn_mclk", NULL);
+
+ clk = clk_register_frac("clcd_syn_clk", "clcd_syn_mclk", 0,
+ SPEAR1310_CLCD_CLK_SYNT, clcd_rtbl,
+ ARRAY_SIZE(clcd_rtbl), &_lock);
+ clk_register_clkdev(clk, "clcd_syn_clk", NULL);
+
+ clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents,
+ ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_CLCD_CLK_SHIFT,
+ SPEAR1310_CLCD_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "clcd_pixel_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "clcd_clk", "clcd_pixel_mclk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_CLCD_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e1000000.clcd");
+
+ /* i2s */
+ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents,
+ ARRAY_SIZE(i2s_src_parents), 0, SPEAR1310_I2S_CLK_CFG,
+ SPEAR1310_I2S_SRC_CLK_SHIFT, SPEAR1310_I2S_SRC_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "i2s_src_mclk", NULL);
+
+ clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", 0,
+ SPEAR1310_I2S_CLK_CFG, &i2s_prs1_masks, i2s_prs1_rtbl,
+ ARRAY_SIZE(i2s_prs1_rtbl), &_lock, NULL);
+ clk_register_clkdev(clk, "i2s_prs1_clk", NULL);
+
+ clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents,
+ ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT,
+ SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_REF_SHIFT,
+ SPEAR1310_I2S_REF_SEL_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "i2s_ref_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2s_ref_pad_clk", "i2s_ref_mclk", 0,
+ SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_I2S_REF_PAD_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, "i2s_ref_pad_clk", NULL);
+
+ clk = clk_register_aux("i2s_sclk_clk", "i2s_sclk_gclk",
+ "i2s_ref_mclk", 0, SPEAR1310_I2S_CLK_CFG,
+ &i2s_sclk_masks, i2s_sclk_rtbl,
+ ARRAY_SIZE(i2s_sclk_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "i2s_sclk_clk", NULL);
+ clk_register_clkdev(clk1, "i2s_sclk_gclk", NULL);
+
+ /* clock derived from ahb clk */
+ clk = clk_register_gate(NULL, "i2c0_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_I2C0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0280000.i2c");
+
+ clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_DMA_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "ea800000.dma");
+ clk_register_clkdev(clk, NULL, "eb000000.dma");
+
+ clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_JPEG_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b2000000.jpeg");
+
+ clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GMAC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e2000000.eth");
+
+ clk = clk_register_gate(NULL, "fsmc_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_FSMC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b0000000.flash");
+
+ clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_SMI_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "ea000000.flash");
+
+ clk = clk_register_gate(NULL, "usbh0_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_UHC0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e4000000.ohci");
+ clk_register_clkdev(clk, NULL, "e4800000.ehci");
+
+ clk = clk_register_gate(NULL, "usbh1_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_UHC1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e5000000.ohci");
+ clk_register_clkdev(clk, NULL, "e5800000.ehci");
+
+ clk = clk_register_gate(NULL, "uoc_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_UOC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e3800000.otg");
+
+ clk = clk_register_gate(NULL, "pcie_sata_0_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_PCIE_SATA_0_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "dw_pcie.0");
+ clk_register_clkdev(clk, NULL, "b1000000.ahci");
+
+ clk = clk_register_gate(NULL, "pcie_sata_1_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_PCIE_SATA_1_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "dw_pcie.1");
+ clk_register_clkdev(clk, NULL, "b1800000.ahci");
+
+ clk = clk_register_gate(NULL, "pcie_sata_2_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_PCIE_SATA_2_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "dw_pcie.2");
+ clk_register_clkdev(clk, NULL, "b4000000.ahci");
+
+ clk = clk_register_gate(NULL, "sysram0_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_SYSRAM0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "sysram0_clk", NULL);
+
+ clk = clk_register_gate(NULL, "sysram1_clk", "ahb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_SYSRAM1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "sysram1_clk", NULL);
+
+ clk = clk_register_aux("adc_syn_clk", "adc_syn_gclk", "ahb_clk",
+ 0, SPEAR1310_ADC_CLK_SYNT, NULL, adc_rtbl,
+ ARRAY_SIZE(adc_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "adc_syn_clk", NULL);
+ clk_register_clkdev(clk1, "adc_syn_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "adc_clk", "adc_syn_gclk",
+ CLK_SET_RATE_PARENT, SPEAR1310_PERIP1_CLK_ENB,
+ SPEAR1310_ADC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e0080000.adc");
+
+ /* clock derived from apb clk */
+ clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_SSP_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0100000.spi");
+
+ clk = clk_register_gate(NULL, "gpio0_clk", "apb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPIO0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0600000.gpio");
+
+ clk = clk_register_gate(NULL, "gpio1_clk", "apb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPIO1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0680000.gpio");
+
+ clk = clk_register_gate(NULL, "i2s0_clk", "apb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_I2S0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0180000.i2s");
+
+ clk = clk_register_gate(NULL, "i2s1_clk", "apb_clk", 0,
+ SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_I2S1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0200000.i2s");
+
+ clk = clk_register_gate(NULL, "kbd_clk", "apb_clk", 0,
+ SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_KBD_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0300000.kbd");
+
+ /* RAS clks */
+ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents,
+ ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1310_PLL_CFG,
+ SPEAR1310_RAS_SYNT0_1_CLK_SHIFT,
+ SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gen_syn0_1_clk", NULL);
+
+ clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents,
+ ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1310_PLL_CFG,
+ SPEAR1310_RAS_SYNT2_3_CLK_SHIFT,
+ SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gen_syn2_3_clk", NULL);
+
+ clk = clk_register_frac("gen_syn0_clk", "gen_syn0_1_clk", 0,
+ SPEAR1310_RAS_CLK_SYNT0, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn0_clk", NULL);
+
+ clk = clk_register_frac("gen_syn1_clk", "gen_syn0_1_clk", 0,
+ SPEAR1310_RAS_CLK_SYNT1, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn1_clk", NULL);
+
+ clk = clk_register_frac("gen_syn2_clk", "gen_syn2_3_clk", 0,
+ SPEAR1310_RAS_CLK_SYNT2, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn2_clk", NULL);
+
+ clk = clk_register_frac("gen_syn3_clk", "gen_syn2_3_clk", 0,
+ SPEAR1310_RAS_CLK_SYNT3, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn3_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_osc_24m_clk", "osc_24m_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_OSC_24M_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_osc_24m_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_osc_25m_clk", "osc_25m_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_OSC_25M_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_osc_25m_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_osc_32k_clk", "osc_32k_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_OSC_32K_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_osc_32k_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_pll2_clk", "pll2_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_PLL2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_pll2_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_pll3_clk", "pll3_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_PLL3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_pll3_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_tx125_clk", "gmii_pad_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_C125M_PAD_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_tx125_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "ras_30m_fixed_clk", "pll5_clk", 0,
+ 30000000);
+ clk = clk_register_gate(NULL, "ras_30m_clk", "ras_30m_fixed_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_C30M_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_30m_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "ras_48m_fixed_clk", "pll5_clk", 0,
+ 48000000);
+ clk = clk_register_gate(NULL, "ras_48m_clk", "ras_48m_fixed_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_C48M_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_48m_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_ahb_clk", "ahb_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_ACLK_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_ahb_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_apb_clk", "apb_clk", 0,
+ SPEAR1310_RAS_CLK_ENB, SPEAR1310_PCLK_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_apb_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "ras_plclk0_clk", NULL, CLK_IS_ROOT,
+ 50000000);
+
+ clk = clk_register_fixed_rate(NULL, "ras_tx50_clk", NULL, CLK_IS_ROOT,
+ 50000000);
+
+ clk = clk_register_gate(NULL, "can0_clk", "apb_clk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_CAN0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "c_can_platform.0");
+
+ clk = clk_register_gate(NULL, "can1_clk", "apb_clk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_CAN1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "c_can_platform.1");
+
+ clk = clk_register_gate(NULL, "ras_smii0_clk", "ras_ahb_clk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_MII0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5c400000.eth");
+
+ clk = clk_register_gate(NULL, "ras_smii1_clk", "ras_ahb_clk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_MII1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5c500000.eth");
+
+ clk = clk_register_gate(NULL, "ras_smii2_clk", "ras_ahb_clk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_MII2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5c600000.eth");
+
+ clk = clk_register_gate(NULL, "ras_rgmii_clk", "ras_ahb_clk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_GMII_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5c700000.eth");
+
+ clk = clk_register_mux(NULL, "smii_rgmii_phy_mclk",
+ smii_rgmii_phy_parents,
+ ARRAY_SIZE(smii_rgmii_phy_parents), 0,
+ SPEAR1310_RAS_CTRL_REG1,
+ SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT,
+ SPEAR1310_PHY_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "stmmacphy.1", NULL);
+ clk_register_clkdev(clk, "stmmacphy.2", NULL);
+ clk_register_clkdev(clk, "stmmacphy.4", NULL);
+
+ clk = clk_register_mux(NULL, "rmii_phy_mclk", rmii_phy_parents,
+ ARRAY_SIZE(rmii_phy_parents), 0,
+ SPEAR1310_RAS_CTRL_REG1, SPEAR1310_RMII_PHY_CLK_SHIFT,
+ SPEAR1310_PHY_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "stmmacphy.3", NULL);
+
+ clk = clk_register_mux(NULL, "uart1_mclk", uart_parents,
+ ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_UART1_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "uart1_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_UART1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5c800000.serial");
+
+ clk = clk_register_mux(NULL, "uart2_mclk", uart_parents,
+ ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_UART2_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "uart2_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart2_clk", "uart2_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_UART2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5c900000.serial");
+
+ clk = clk_register_mux(NULL, "uart3_mclk", uart_parents,
+ ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_UART3_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "uart3_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart3_clk", "uart3_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_UART3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5ca00000.serial");
+
+ clk = clk_register_mux(NULL, "uart4_mclk", uart_parents,
+ ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_UART4_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "uart4_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart4_clk", "uart4_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_UART4_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5cb00000.serial");
+
+ clk = clk_register_mux(NULL, "uart5_mclk", uart_parents,
+ ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_UART5_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "uart5_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart5_clk", "uart5_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_UART5_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5cc00000.serial");
+
+ clk = clk_register_mux(NULL, "i2c1_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C1_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c1_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c1_clk", "i2c1_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5cd00000.i2c");
+
+ clk = clk_register_mux(NULL, "i2c2_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C2_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c2_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c2_clk", "i2c2_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5ce00000.i2c");
+
+ clk = clk_register_mux(NULL, "i2c3_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C3_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c3_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c3_clk", "i2c3_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5cf00000.i2c");
+
+ clk = clk_register_mux(NULL, "i2c4_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C4_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c4_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c4_clk", "i2c4_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C4_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5d000000.i2c");
+
+ clk = clk_register_mux(NULL, "i2c5_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C5_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c5_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c5_clk", "i2c5_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C5_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5d100000.i2c");
+
+ clk = clk_register_mux(NULL, "i2c6_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C6_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c6_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c6_clk", "i2c6_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C6_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5d200000.i2c");
+
+ clk = clk_register_mux(NULL, "i2c7_mclk", i2c_parents,
+ ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_I2C7_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "i2c7_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2c7_clk", "i2c7_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_I2C7_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5d300000.i2c");
+
+ clk = clk_register_mux(NULL, "ssp1_mclk", ssp1_parents,
+ ARRAY_SIZE(ssp1_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_SSP1_CLK_SHIFT, SPEAR1310_SSP1_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ssp1_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "ssp1_clk", "ssp1_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_SSP1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "5d400000.spi");
+
+ clk = clk_register_mux(NULL, "pci_mclk", pci_parents,
+ ARRAY_SIZE(pci_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_PCI_CLK_SHIFT, SPEAR1310_PCI_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "pci_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "pci_clk", "pci_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_PCI_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "pci");
+
+ clk = clk_register_mux(NULL, "tdm1_mclk", tdm_parents,
+ ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_TDM1_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "tdm1_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "tdm1_clk", "tdm1_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_TDM1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "tdm_hdlc.0");
+
+ clk = clk_register_mux(NULL, "tdm2_mclk", tdm_parents,
+ ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0,
+ SPEAR1310_TDM2_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "tdm2_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "tdm2_clk", "tdm2_mclk", 0,
+ SPEAR1310_RAS_SW_CLK_CTRL, SPEAR1310_TDM2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "tdm_hdlc.1");
+}
diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c
new file mode 100644
index 000000000..9d0b3949d
--- /dev/null
+++ b/drivers/clk/spear/spear1340_clock.c
@@ -0,0 +1,1011 @@
+/*
+ * arch/arm/mach-spear13xx/spear1340_clock.c
+ *
+ * SPEAr1340 machine clock framework source file
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock_types.h>
+#include "clk.h"
+
+/* Clock Configuration Registers */
+#define SPEAR1340_SYS_CLK_CTRL (misc_base + 0x200)
+ #define SPEAR1340_HCLK_SRC_SEL_SHIFT 27
+ #define SPEAR1340_HCLK_SRC_SEL_MASK 1
+ #define SPEAR1340_SCLK_SRC_SEL_SHIFT 23
+ #define SPEAR1340_SCLK_SRC_SEL_MASK 3
+
+/* PLL related registers and bit values */
+#define SPEAR1340_PLL_CFG (misc_base + 0x210)
+ /* PLL_CFG bit values */
+ #define SPEAR1340_CLCD_SYNT_CLK_MASK 1
+ #define SPEAR1340_CLCD_SYNT_CLK_SHIFT 31
+ #define SPEAR1340_GEN_SYNT2_3_CLK_SHIFT 29
+ #define SPEAR1340_GEN_SYNT_CLK_MASK 2
+ #define SPEAR1340_GEN_SYNT0_1_CLK_SHIFT 27
+ #define SPEAR1340_PLL_CLK_MASK 2
+ #define SPEAR1340_PLL3_CLK_SHIFT 24
+ #define SPEAR1340_PLL2_CLK_SHIFT 22
+ #define SPEAR1340_PLL1_CLK_SHIFT 20
+
+#define SPEAR1340_PLL1_CTR (misc_base + 0x214)
+#define SPEAR1340_PLL1_FRQ (misc_base + 0x218)
+#define SPEAR1340_PLL2_CTR (misc_base + 0x220)
+#define SPEAR1340_PLL2_FRQ (misc_base + 0x224)
+#define SPEAR1340_PLL3_CTR (misc_base + 0x22C)
+#define SPEAR1340_PLL3_FRQ (misc_base + 0x230)
+#define SPEAR1340_PLL4_CTR (misc_base + 0x238)
+#define SPEAR1340_PLL4_FRQ (misc_base + 0x23C)
+#define SPEAR1340_PERIP_CLK_CFG (misc_base + 0x244)
+ /* PERIP_CLK_CFG bit values */
+ #define SPEAR1340_SPDIF_CLK_MASK 1
+ #define SPEAR1340_SPDIF_OUT_CLK_SHIFT 15
+ #define SPEAR1340_SPDIF_IN_CLK_SHIFT 14
+ #define SPEAR1340_GPT3_CLK_SHIFT 13
+ #define SPEAR1340_GPT2_CLK_SHIFT 12
+ #define SPEAR1340_GPT_CLK_MASK 1
+ #define SPEAR1340_GPT1_CLK_SHIFT 9
+ #define SPEAR1340_GPT0_CLK_SHIFT 8
+ #define SPEAR1340_UART_CLK_MASK 2
+ #define SPEAR1340_UART1_CLK_SHIFT 6
+ #define SPEAR1340_UART0_CLK_SHIFT 4
+ #define SPEAR1340_CLCD_CLK_MASK 2
+ #define SPEAR1340_CLCD_CLK_SHIFT 2
+ #define SPEAR1340_C3_CLK_MASK 1
+ #define SPEAR1340_C3_CLK_SHIFT 1
+
+#define SPEAR1340_GMAC_CLK_CFG (misc_base + 0x248)
+ #define SPEAR1340_GMAC_PHY_CLK_MASK 1
+ #define SPEAR1340_GMAC_PHY_CLK_SHIFT 2
+ #define SPEAR1340_GMAC_PHY_INPUT_CLK_MASK 2
+ #define SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT 0
+
+#define SPEAR1340_I2S_CLK_CFG (misc_base + 0x24C)
+ /* I2S_CLK_CFG register mask */
+ #define SPEAR1340_I2S_SCLK_X_MASK 0x1F
+ #define SPEAR1340_I2S_SCLK_X_SHIFT 27
+ #define SPEAR1340_I2S_SCLK_Y_MASK 0x1F
+ #define SPEAR1340_I2S_SCLK_Y_SHIFT 22
+ #define SPEAR1340_I2S_SCLK_EQ_SEL_SHIFT 21
+ #define SPEAR1340_I2S_SCLK_SYNTH_ENB 20
+ #define SPEAR1340_I2S_PRS1_CLK_X_MASK 0xFF
+ #define SPEAR1340_I2S_PRS1_CLK_X_SHIFT 12
+ #define SPEAR1340_I2S_PRS1_CLK_Y_MASK 0xFF
+ #define SPEAR1340_I2S_PRS1_CLK_Y_SHIFT 4
+ #define SPEAR1340_I2S_PRS1_EQ_SEL_SHIFT 3
+ #define SPEAR1340_I2S_REF_SEL_MASK 1
+ #define SPEAR1340_I2S_REF_SHIFT 2
+ #define SPEAR1340_I2S_SRC_CLK_MASK 2
+ #define SPEAR1340_I2S_SRC_CLK_SHIFT 0
+
+#define SPEAR1340_C3_CLK_SYNT (misc_base + 0x250)
+#define SPEAR1340_UART0_CLK_SYNT (misc_base + 0x254)
+#define SPEAR1340_UART1_CLK_SYNT (misc_base + 0x258)
+#define SPEAR1340_GMAC_CLK_SYNT (misc_base + 0x25C)
+#define SPEAR1340_SDHCI_CLK_SYNT (misc_base + 0x260)
+#define SPEAR1340_CFXD_CLK_SYNT (misc_base + 0x264)
+#define SPEAR1340_ADC_CLK_SYNT (misc_base + 0x270)
+#define SPEAR1340_AMBA_CLK_SYNT (misc_base + 0x274)
+#define SPEAR1340_CLCD_CLK_SYNT (misc_base + 0x27C)
+#define SPEAR1340_SYS_CLK_SYNT (misc_base + 0x284)
+#define SPEAR1340_GEN_CLK_SYNT0 (misc_base + 0x28C)
+#define SPEAR1340_GEN_CLK_SYNT1 (misc_base + 0x294)
+#define SPEAR1340_GEN_CLK_SYNT2 (misc_base + 0x29C)
+#define SPEAR1340_GEN_CLK_SYNT3 (misc_base + 0x304)
+#define SPEAR1340_PERIP1_CLK_ENB (misc_base + 0x30C)
+ #define SPEAR1340_RTC_CLK_ENB 31
+ #define SPEAR1340_ADC_CLK_ENB 30
+ #define SPEAR1340_C3_CLK_ENB 29
+ #define SPEAR1340_CLCD_CLK_ENB 27
+ #define SPEAR1340_DMA_CLK_ENB 25
+ #define SPEAR1340_GPIO1_CLK_ENB 24
+ #define SPEAR1340_GPIO0_CLK_ENB 23
+ #define SPEAR1340_GPT1_CLK_ENB 22
+ #define SPEAR1340_GPT0_CLK_ENB 21
+ #define SPEAR1340_I2S_PLAY_CLK_ENB 20
+ #define SPEAR1340_I2S_REC_CLK_ENB 19
+ #define SPEAR1340_I2C0_CLK_ENB 18
+ #define SPEAR1340_SSP_CLK_ENB 17
+ #define SPEAR1340_UART0_CLK_ENB 15
+ #define SPEAR1340_PCIE_SATA_CLK_ENB 12
+ #define SPEAR1340_UOC_CLK_ENB 11
+ #define SPEAR1340_UHC1_CLK_ENB 10
+ #define SPEAR1340_UHC0_CLK_ENB 9
+ #define SPEAR1340_GMAC_CLK_ENB 8
+ #define SPEAR1340_CFXD_CLK_ENB 7
+ #define SPEAR1340_SDHCI_CLK_ENB 6
+ #define SPEAR1340_SMI_CLK_ENB 5
+ #define SPEAR1340_FSMC_CLK_ENB 4
+ #define SPEAR1340_SYSRAM0_CLK_ENB 3
+ #define SPEAR1340_SYSRAM1_CLK_ENB 2
+ #define SPEAR1340_SYSROM_CLK_ENB 1
+ #define SPEAR1340_BUS_CLK_ENB 0
+
+#define SPEAR1340_PERIP2_CLK_ENB (misc_base + 0x310)
+ #define SPEAR1340_THSENS_CLK_ENB 8
+ #define SPEAR1340_I2S_REF_PAD_CLK_ENB 7
+ #define SPEAR1340_ACP_CLK_ENB 6
+ #define SPEAR1340_GPT3_CLK_ENB 5
+ #define SPEAR1340_GPT2_CLK_ENB 4
+ #define SPEAR1340_KBD_CLK_ENB 3
+ #define SPEAR1340_CPU_DBG_CLK_ENB 2
+ #define SPEAR1340_DDR_CORE_CLK_ENB 1
+ #define SPEAR1340_DDR_CTRL_CLK_ENB 0
+
+#define SPEAR1340_PERIP3_CLK_ENB (misc_base + 0x314)
+ #define SPEAR1340_PLGPIO_CLK_ENB 18
+ #define SPEAR1340_VIDEO_DEC_CLK_ENB 16
+ #define SPEAR1340_VIDEO_ENC_CLK_ENB 15
+ #define SPEAR1340_SPDIF_OUT_CLK_ENB 13
+ #define SPEAR1340_SPDIF_IN_CLK_ENB 12
+ #define SPEAR1340_VIDEO_IN_CLK_ENB 11
+ #define SPEAR1340_CAM0_CLK_ENB 10
+ #define SPEAR1340_CAM1_CLK_ENB 9
+ #define SPEAR1340_CAM2_CLK_ENB 8
+ #define SPEAR1340_CAM3_CLK_ENB 7
+ #define SPEAR1340_MALI_CLK_ENB 6
+ #define SPEAR1340_CEC0_CLK_ENB 5
+ #define SPEAR1340_CEC1_CLK_ENB 4
+ #define SPEAR1340_PWM_CLK_ENB 3
+ #define SPEAR1340_I2C1_CLK_ENB 2
+ #define SPEAR1340_UART1_CLK_ENB 1
+
+static DEFINE_SPINLOCK(_lock);
+
+/* pll rate configuration table, in ascending order of rates */
+static struct pll_rate_tbl pll_rtbl[] = {
+ /* PCLK 24MHz */
+ {.mode = 0, .m = 0x83, .n = 0x04, .p = 0x5}, /* vco 1572, pll 49.125 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x3}, /* vco 1000, pll 125 MHz */
+ {.mode = 0, .m = 0x64, .n = 0x06, .p = 0x1}, /* vco 800, pll 400 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x1}, /* vco 1000, pll 500 MHz */
+ {.mode = 0, .m = 0xA6, .n = 0x06, .p = 0x1}, /* vco 1328, pll 664 MHz */
+ {.mode = 0, .m = 0xC8, .n = 0x06, .p = 0x1}, /* vco 1600, pll 800 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x0}, /* vco 1, pll 1 GHz */
+ {.mode = 0, .m = 0x96, .n = 0x06, .p = 0x0}, /* vco 1200, pll 1200 MHz */
+};
+
+/* vco-pll4 rate configuration table, in ascending order of rates */
+static struct pll_rate_tbl pll4_rtbl[] = {
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x2}, /* vco 1000, pll 250 MHz */
+ {.mode = 0, .m = 0xA6, .n = 0x06, .p = 0x2}, /* vco 1328, pll 332 MHz */
+ {.mode = 0, .m = 0xC8, .n = 0x06, .p = 0x2}, /* vco 1600, pll 400 MHz */
+ {.mode = 0, .m = 0x7D, .n = 0x06, .p = 0x0}, /* vco 1, pll 1 GHz */
+};
+
+/*
+ * All below entries generate 166 MHz for
+ * different values of vco1div2
+ */
+static struct frac_rate_tbl amba_synth_rtbl[] = {
+ {.div = 0x073A8}, /* for vco1div2 = 600 MHz */
+ {.div = 0x06062}, /* for vco1div2 = 500 MHz */
+ {.div = 0x04D1B}, /* for vco1div2 = 400 MHz */
+ {.div = 0x04000}, /* for vco1div2 = 332 MHz */
+ {.div = 0x03031}, /* for vco1div2 = 250 MHz */
+ {.div = 0x0268D}, /* for vco1div2 = 200 MHz */
+};
+
+/*
+ * Synthesizer Clock derived from vcodiv2. This clock is one of the
+ * possible clocks to feed cpu directly.
+ * We can program this synthesizer to make cpu run on different clock
+ * frequencies.
+ * Following table provides configuration values to let cpu run on 200,
+ * 250, 332, 400 or 500 MHz considering different possibilites of input
+ * (vco1div2) clock.
+ *
+ * --------------------------------------------------------------------
+ * vco1div2(Mhz) fout(Mhz) cpuclk = fout/2 div
+ * --------------------------------------------------------------------
+ * 400 200 100 0x04000
+ * 400 250 125 0x03333
+ * 400 332 166 0x0268D
+ * 400 400 200 0x02000
+ * --------------------------------------------------------------------
+ * 500 200 100 0x05000
+ * 500 250 125 0x04000
+ * 500 332 166 0x03031
+ * 500 400 200 0x02800
+ * 500 500 250 0x02000
+ * --------------------------------------------------------------------
+ * 600 200 100 0x06000
+ * 600 250 125 0x04CCE
+ * 600 332 166 0x039D5
+ * 600 400 200 0x03000
+ * 600 500 250 0x02666
+ * --------------------------------------------------------------------
+ * 664 200 100 0x06a38
+ * 664 250 125 0x054FD
+ * 664 332 166 0x04000
+ * 664 400 200 0x0351E
+ * 664 500 250 0x02A7E
+ * --------------------------------------------------------------------
+ * 800 200 100 0x08000
+ * 800 250 125 0x06666
+ * 800 332 166 0x04D18
+ * 800 400 200 0x04000
+ * 800 500 250 0x03333
+ * --------------------------------------------------------------------
+ * sys rate configuration table is in descending order of divisor.
+ */
+static struct frac_rate_tbl sys_synth_rtbl[] = {
+ {.div = 0x08000},
+ {.div = 0x06a38},
+ {.div = 0x06666},
+ {.div = 0x06000},
+ {.div = 0x054FD},
+ {.div = 0x05000},
+ {.div = 0x04D18},
+ {.div = 0x04CCE},
+ {.div = 0x04000},
+ {.div = 0x039D5},
+ {.div = 0x0351E},
+ {.div = 0x03333},
+ {.div = 0x03031},
+ {.div = 0x03000},
+ {.div = 0x02A7E},
+ {.div = 0x02800},
+ {.div = 0x0268D},
+ {.div = 0x02666},
+ {.div = 0x02000},
+};
+
+/* aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl aux_rtbl[] = {
+ /* 12.29MHz for vic1div2=600MHz and 10.24MHz for VCO1div2=500MHz */
+ {.xscale = 5, .yscale = 122, .eq = 0},
+ /* 14.70MHz for vic1div2=600MHz and 12.29MHz for VCO1div2=500MHz */
+ {.xscale = 10, .yscale = 204, .eq = 0},
+ /* 48MHz for vic1div2=600MHz and 40 MHz for VCO1div2=500MHz */
+ {.xscale = 4, .yscale = 25, .eq = 0},
+ /* 57.14MHz for vic1div2=600MHz and 48 MHz for VCO1div2=500MHz */
+ {.xscale = 4, .yscale = 21, .eq = 0},
+ /* 83.33MHz for vic1div2=600MHz and 69.44MHz for VCO1div2=500MHz */
+ {.xscale = 5, .yscale = 18, .eq = 0},
+ /* 100MHz for vic1div2=600MHz and 83.33 MHz for VCO1div2=500MHz */
+ {.xscale = 2, .yscale = 6, .eq = 0},
+ /* 125MHz for vic1div2=600MHz and 104.1MHz for VCO1div2=500MHz */
+ {.xscale = 5, .yscale = 12, .eq = 0},
+ /* 150MHz for vic1div2=600MHz and 125MHz for VCO1div2=500MHz */
+ {.xscale = 2, .yscale = 4, .eq = 0},
+ /* 166MHz for vic1div2=600MHz and 138.88MHz for VCO1div2=500MHz */
+ {.xscale = 5, .yscale = 18, .eq = 1},
+ /* 200MHz for vic1div2=600MHz and 166MHz for VCO1div2=500MHz */
+ {.xscale = 1, .yscale = 3, .eq = 1},
+ /* 250MHz for vic1div2=600MHz and 208.33MHz for VCO1div2=500MHz */
+ {.xscale = 5, .yscale = 12, .eq = 1},
+ /* 300MHz for vic1div2=600MHz and 250MHz for VCO1div2=500MHz */
+ {.xscale = 1, .yscale = 2, .eq = 1},
+};
+
+/* gmac rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl gmac_rtbl[] = {
+ /* For gmac phy input clk */
+ {.xscale = 2, .yscale = 6, .eq = 0}, /* divided by 6 */
+ {.xscale = 2, .yscale = 4, .eq = 0}, /* divided by 4 */
+ {.xscale = 1, .yscale = 3, .eq = 1}, /* divided by 3 */
+ {.xscale = 1, .yscale = 2, .eq = 1}, /* divided by 2 */
+};
+
+/* clcd rate configuration table, in ascending order of rates */
+static struct frac_rate_tbl clcd_rtbl[] = {
+ {.div = 0x18000}, /* 25 Mhz , for vc01div4 = 300 MHz*/
+ {.div = 0x1638E}, /* 27 Mhz , for vc01div4 = 300 MHz*/
+ {.div = 0x14000}, /* 25 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x1284B}, /* 27 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x0D8D3}, /* 58 Mhz , for vco1div4 = 393 MHz */
+ {.div = 0x0B72C}, /* 58 Mhz , for vco1div4 = 332 MHz */
+ {.div = 0x0A584}, /* 58 Mhz , for vco1div4 = 300 MHz */
+ {.div = 0x093B1}, /* 65 Mhz , for vc01div4 = 300 MHz*/
+ {.div = 0x089EE}, /* 58 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x081BA}, /* 74 Mhz , for vc01div4 = 300 MHz*/
+ {.div = 0x07BA0}, /* 65 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x06f1C}, /* 72 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x06E58}, /* 58 Mhz , for vco1div4 = 200 MHz */
+ {.div = 0x06c1B}, /* 74 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x058E3}, /* 108 Mhz , for vc01div4 = 300 MHz*/
+ {.div = 0x04A12}, /* 108 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x040A5}, /* 148.5 Mhz , for vc01div4 = 300 MHz*/
+ {.div = 0x0378E}, /* 144 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x0360D}, /* 148 Mhz , for vc01div4 = 250 MHz*/
+ {.div = 0x035E0}, /* 148.5 MHz, for vc01div4 = 250 MHz*/
+};
+
+/* i2s prescaler1 masks */
+static struct aux_clk_masks i2s_prs1_masks = {
+ .eq_sel_mask = AUX_EQ_SEL_MASK,
+ .eq_sel_shift = SPEAR1340_I2S_PRS1_EQ_SEL_SHIFT,
+ .eq1_mask = AUX_EQ1_SEL,
+ .eq2_mask = AUX_EQ2_SEL,
+ .xscale_sel_mask = SPEAR1340_I2S_PRS1_CLK_X_MASK,
+ .xscale_sel_shift = SPEAR1340_I2S_PRS1_CLK_X_SHIFT,
+ .yscale_sel_mask = SPEAR1340_I2S_PRS1_CLK_Y_MASK,
+ .yscale_sel_shift = SPEAR1340_I2S_PRS1_CLK_Y_SHIFT,
+};
+
+/* i2s sclk (bit clock) syynthesizers masks */
+static struct aux_clk_masks i2s_sclk_masks = {
+ .eq_sel_mask = AUX_EQ_SEL_MASK,
+ .eq_sel_shift = SPEAR1340_I2S_SCLK_EQ_SEL_SHIFT,
+ .eq1_mask = AUX_EQ1_SEL,
+ .eq2_mask = AUX_EQ2_SEL,
+ .xscale_sel_mask = SPEAR1340_I2S_SCLK_X_MASK,
+ .xscale_sel_shift = SPEAR1340_I2S_SCLK_X_SHIFT,
+ .yscale_sel_mask = SPEAR1340_I2S_SCLK_Y_MASK,
+ .yscale_sel_shift = SPEAR1340_I2S_SCLK_Y_SHIFT,
+ .enable_bit = SPEAR1340_I2S_SCLK_SYNTH_ENB,
+};
+
+/* i2s prs1 aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl i2s_prs1_rtbl[] = {
+ /* For parent clk = 49.152 MHz */
+ {.xscale = 1, .yscale = 12, .eq = 0}, /* 2.048 MHz, smp freq = 8Khz */
+ {.xscale = 11, .yscale = 96, .eq = 0}, /* 2.816 MHz, smp freq = 11Khz */
+ {.xscale = 1, .yscale = 6, .eq = 0}, /* 4.096 MHz, smp freq = 16Khz */
+ {.xscale = 11, .yscale = 48, .eq = 0}, /* 5.632 MHz, smp freq = 22Khz */
+
+ /*
+ * with parent clk = 49.152, freq gen is 8.192 MHz, smp freq = 32Khz
+ * with parent clk = 12.288, freq gen is 2.048 MHz, smp freq = 8Khz
+ */
+ {.xscale = 1, .yscale = 3, .eq = 0},
+
+ /* For parent clk = 49.152 MHz */
+ {.xscale = 17, .yscale = 37, .eq = 0}, /* 11.289 MHz, smp freq = 44Khz*/
+ {.xscale = 1, .yscale = 2, .eq = 0}, /* 12.288 MHz, smp freq = 48Khz*/
+};
+
+/* i2s sclk aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl i2s_sclk_rtbl[] = {
+ /* For sclk = ref_clk * x/2/y */
+ {.xscale = 1, .yscale = 4, .eq = 0},
+ {.xscale = 1, .yscale = 2, .eq = 0},
+};
+
+/* adc rate configuration table, in ascending order of rates */
+/* possible adc range is 2.5 MHz to 20 MHz. */
+static struct aux_rate_tbl adc_rtbl[] = {
+ /* For ahb = 166.67 MHz */
+ {.xscale = 1, .yscale = 31, .eq = 0}, /* 2.68 MHz */
+ {.xscale = 2, .yscale = 21, .eq = 0}, /* 7.94 MHz */
+ {.xscale = 4, .yscale = 21, .eq = 0}, /* 15.87 MHz */
+ {.xscale = 10, .yscale = 42, .eq = 0}, /* 19.84 MHz */
+};
+
+/* General synth rate configuration table, in ascending order of rates */
+static struct frac_rate_tbl gen_rtbl[] = {
+ {.div = 0x1A92B}, /* 22.5792 MHz for vco1div4=300 MHz*/
+ {.div = 0x186A0}, /* 24.576 MHz for vco1div4=300 MHz*/
+ {.div = 0x18000}, /* 25 MHz for vco1div4=300 MHz*/
+ {.div = 0x1624E}, /* 22.5792 MHz for vco1div4=250 MHz*/
+ {.div = 0x14585}, /* 24.576 MHz for vco1div4=250 MHz*/
+ {.div = 0x14000}, /* 25 MHz for vco1div4=250 MHz*/
+ {.div = 0x0D495}, /* 45.1584 MHz for vco1div4=300 MHz*/
+ {.div = 0x0C000}, /* 50 MHz for vco1div4=300 MHz*/
+ {.div = 0x0B127}, /* 45.1584 MHz for vco1div4=250 MHz*/
+ {.div = 0x0A000}, /* 50 MHz for vco1div4=250 MHz*/
+ {.div = 0x07530}, /* 81.92 MHz for vco1div4=300 MHz*/
+ {.div = 0x061A8}, /* 81.92 MHz for vco1div4=250 MHz*/
+ {.div = 0x06000}, /* 100 MHz for vco1div4=300 MHz*/
+ {.div = 0x05000}, /* 100 MHz for vco1div4=250 MHz*/
+ {.div = 0x03000}, /* 200 MHz for vco1div4=300 MHz*/
+ {.div = 0x02DB6}, /* 210 MHz for vco1div4=300 MHz*/
+ {.div = 0x02BA2}, /* 220 MHz for vco1div4=300 MHz*/
+ {.div = 0x029BD}, /* 230 MHz for vco1div4=300 MHz*/
+ {.div = 0x02800}, /* 200 MHz for vco1div4=250 MHz*/
+ {.div = 0x02666}, /* 250 MHz for vco1div4=300 MHz*/
+ {.div = 0x02620}, /* 210 MHz for vco1div4=250 MHz*/
+ {.div = 0x02460}, /* 220 MHz for vco1div4=250 MHz*/
+ {.div = 0x022C0}, /* 230 MHz for vco1div4=250 MHz*/
+ {.div = 0x02160}, /* 240 MHz for vco1div4=250 MHz*/
+ {.div = 0x02000}, /* 250 MHz for vco1div4=250 MHz*/
+};
+
+/* clock parents */
+static const char *vco_parents[] = { "osc_24m_clk", "osc_25m_clk", };
+static const char *sys_parents[] = { "pll1_clk", "pll1_clk", "pll1_clk",
+ "pll1_clk", "sys_syn_clk", "sys_syn_clk", "pll2_clk", "pll3_clk", };
+static const char *ahb_parents[] = { "cpu_div3_clk", "amba_syn_clk", };
+static const char *gpt_parents[] = { "osc_24m_clk", "apb_clk", };
+static const char *uart0_parents[] = { "pll5_clk", "osc_24m_clk",
+ "uart0_syn_gclk", };
+static const char *uart1_parents[] = { "pll5_clk", "osc_24m_clk",
+ "uart1_syn_gclk", };
+static const char *c3_parents[] = { "pll5_clk", "c3_syn_gclk", };
+static const char *gmac_phy_input_parents[] = { "gmii_pad_clk", "pll2_clk",
+ "osc_25m_clk", };
+static const char *gmac_phy_parents[] = { "phy_input_mclk", "phy_syn_gclk", };
+static const char *clcd_synth_parents[] = { "vco1div4_clk", "pll2_clk", };
+static const char *clcd_pixel_parents[] = { "pll5_clk", "clcd_syn_clk", };
+static const char *i2s_src_parents[] = { "vco1div2_clk", "pll2_clk", "pll3_clk",
+ "i2s_src_pad_clk", };
+static const char *i2s_ref_parents[] = { "i2s_src_mclk", "i2s_prs1_clk", };
+static const char *spdif_out_parents[] = { "i2s_src_pad_clk", "gen_syn2_clk", };
+static const char *spdif_in_parents[] = { "pll2_clk", "gen_syn3_clk", };
+
+static const char *gen_synth0_1_parents[] = { "vco1div4_clk", "vco3div2_clk",
+ "pll3_clk", };
+static const char *gen_synth2_3_parents[] = { "vco1div4_clk", "vco2div2_clk",
+ "pll2_clk", };
+
+void __init spear1340_clk_init(void __iomem *misc_base)
+{
+ struct clk *clk, *clk1;
+
+ clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT,
+ 32000);
+ clk_register_clkdev(clk, "osc_32k_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "osc_24m_clk", NULL, CLK_IS_ROOT,
+ 24000000);
+ clk_register_clkdev(clk, "osc_24m_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "osc_25m_clk", NULL, CLK_IS_ROOT,
+ 25000000);
+ clk_register_clkdev(clk, "osc_25m_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "gmii_pad_clk", NULL, CLK_IS_ROOT,
+ 125000000);
+ clk_register_clkdev(clk, "gmii_pad_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "i2s_src_pad_clk", NULL,
+ CLK_IS_ROOT, 12288000);
+ clk_register_clkdev(clk, "i2s_src_pad_clk", NULL);
+
+ /* clock derived from 32 KHz osc clk */
+ clk = clk_register_gate(NULL, "rtc-spear", "osc_32k_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_RTC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0580000.rtc");
+
+ /* clock derived from 24 or 25 MHz osc clk */
+ /* vco-pll */
+ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents,
+ ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG,
+ SPEAR1340_PLL1_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "vco1_mclk", NULL);
+ clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0,
+ SPEAR1340_PLL1_CTR, SPEAR1340_PLL1_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco1_clk", NULL);
+ clk_register_clkdev(clk1, "pll1_clk", NULL);
+
+ clk = clk_register_mux(NULL, "vco2_mclk", vco_parents,
+ ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG,
+ SPEAR1340_PLL2_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "vco2_mclk", NULL);
+ clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0,
+ SPEAR1340_PLL2_CTR, SPEAR1340_PLL2_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco2_clk", NULL);
+ clk_register_clkdev(clk1, "pll2_clk", NULL);
+
+ clk = clk_register_mux(NULL, "vco3_mclk", vco_parents,
+ ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG,
+ SPEAR1340_PLL3_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "vco3_mclk", NULL);
+ clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0,
+ SPEAR1340_PLL3_CTR, SPEAR1340_PLL3_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco3_clk", NULL);
+ clk_register_clkdev(clk1, "pll3_clk", NULL);
+
+ clk = clk_register_vco_pll("vco4_clk", "pll4_clk", NULL, "osc_24m_clk",
+ 0, SPEAR1340_PLL4_CTR, SPEAR1340_PLL4_FRQ, pll4_rtbl,
+ ARRAY_SIZE(pll4_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco4_clk", NULL);
+ clk_register_clkdev(clk1, "pll4_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll5_clk", "osc_24m_clk", 0,
+ 48000000);
+ clk_register_clkdev(clk, "pll5_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll6_clk", "osc_25m_clk", 0,
+ 25000000);
+ clk_register_clkdev(clk, "pll6_clk", NULL);
+
+ /* vco div n clocks */
+ clk = clk_register_fixed_factor(NULL, "vco1div2_clk", "vco1_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "vco1div2_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vco1div4_clk", "vco1_clk", 0, 1,
+ 4);
+ clk_register_clkdev(clk, "vco1div4_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vco2div2_clk", "vco2_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "vco2div2_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vco3div2_clk", "vco3_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "vco3div2_clk", NULL);
+
+ /* peripherals */
+ clk_register_fixed_factor(NULL, "thermal_clk", "osc_24m_clk", 0, 1,
+ 128);
+ clk = clk_register_gate(NULL, "thermal_gclk", "thermal_clk", 0,
+ SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_THSENS_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e07008c4.thermal");
+
+ /* clock derived from pll4 clk */
+ clk = clk_register_fixed_factor(NULL, "ddr_clk", "pll4_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "ddr_clk", NULL);
+
+ /* clock derived from pll1 clk */
+ clk = clk_register_frac("sys_syn_clk", "vco1div2_clk", 0,
+ SPEAR1340_SYS_CLK_SYNT, sys_synth_rtbl,
+ ARRAY_SIZE(sys_synth_rtbl), &_lock);
+ clk_register_clkdev(clk, "sys_syn_clk", NULL);
+
+ clk = clk_register_frac("amba_syn_clk", "vco1div2_clk", 0,
+ SPEAR1340_AMBA_CLK_SYNT, amba_synth_rtbl,
+ ARRAY_SIZE(amba_synth_rtbl), &_lock);
+ clk_register_clkdev(clk, "amba_syn_clk", NULL);
+
+ clk = clk_register_mux(NULL, "sys_mclk", sys_parents,
+ ARRAY_SIZE(sys_parents), 0, SPEAR1340_SYS_CLK_CTRL,
+ SPEAR1340_SCLK_SRC_SEL_SHIFT,
+ SPEAR1340_SCLK_SRC_SEL_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "sys_mclk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "cpu_clk", "sys_mclk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "cpu_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "cpu_div3_clk", "cpu_clk", 0, 1,
+ 3);
+ clk_register_clkdev(clk, "cpu_div3_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "wdt_clk", "cpu_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, NULL, "ec800620.wdt");
+
+ clk = clk_register_fixed_factor(NULL, "smp_twd_clk", "cpu_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, NULL, "smp_twd");
+
+ clk = clk_register_mux(NULL, "ahb_clk", ahb_parents,
+ ARRAY_SIZE(ahb_parents), 0, SPEAR1340_SYS_CLK_CTRL,
+ SPEAR1340_HCLK_SRC_SEL_SHIFT,
+ SPEAR1340_HCLK_SRC_SEL_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "ahb_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
+ 2);
+ clk_register_clkdev(clk, "apb_clk", NULL);
+
+ /* gpt clocks */
+ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
+ SPEAR1340_GPT0_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt0_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt0");
+
+ clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
+ SPEAR1340_GPT1_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt1_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt1");
+
+ clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
+ SPEAR1340_GPT2_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt2_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0,
+ SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt2");
+
+ clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents,
+ ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
+ SPEAR1340_GPT3_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gpt3_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0,
+ SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt3");
+
+ /* others */
+ clk = clk_register_aux("uart0_syn_clk", "uart0_syn_gclk",
+ "vco1div2_clk", 0, SPEAR1340_UART0_CLK_SYNT, NULL,
+ aux_rtbl, ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "uart0_syn_clk", NULL);
+ clk_register_clkdev(clk1, "uart0_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents,
+ ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART0_CLK_SHIFT,
+ SPEAR1340_UART_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "uart0_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart0_clk", "uart0_mclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP1_CLK_ENB,
+ SPEAR1340_UART0_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e0000000.serial");
+
+ clk = clk_register_aux("uart1_syn_clk", "uart1_syn_gclk",
+ "vco1div2_clk", 0, SPEAR1340_UART1_CLK_SYNT, NULL,
+ aux_rtbl, ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "uart1_syn_clk", NULL);
+ clk_register_clkdev(clk1, "uart1_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "uart1_mclk", uart1_parents,
+ ARRAY_SIZE(uart1_parents), 0, SPEAR1340_PERIP_CLK_CFG,
+ SPEAR1340_UART1_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "uart1_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_UART1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b4100000.serial");
+
+ clk = clk_register_aux("sdhci_syn_clk", "sdhci_syn_gclk",
+ "vco1div2_clk", 0, SPEAR1340_SDHCI_CLK_SYNT, NULL,
+ aux_rtbl, ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "sdhci_syn_clk", NULL);
+ clk_register_clkdev(clk1, "sdhci_syn_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "sdhci_clk", "sdhci_syn_gclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP1_CLK_ENB,
+ SPEAR1340_SDHCI_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "b3000000.sdhci");
+
+ clk = clk_register_aux("cfxd_syn_clk", "cfxd_syn_gclk", "vco1div2_clk",
+ 0, SPEAR1340_CFXD_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "cfxd_syn_clk", NULL);
+ clk_register_clkdev(clk1, "cfxd_syn_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "cfxd_clk", "cfxd_syn_gclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP1_CLK_ENB,
+ SPEAR1340_CFXD_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "b2800000.cf");
+ clk_register_clkdev(clk, NULL, "arasan_xd");
+
+ clk = clk_register_aux("c3_syn_clk", "c3_syn_gclk", "vco1div2_clk", 0,
+ SPEAR1340_C3_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "c3_syn_clk", NULL);
+ clk_register_clkdev(clk1, "c3_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "c3_mclk", c3_parents,
+ ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_C3_CLK_SHIFT,
+ SPEAR1340_C3_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "c3_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "c3_clk", "c3_mclk", CLK_SET_RATE_PARENT,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_C3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e1800000.c3");
+
+ /* gmac */
+ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents,
+ ARRAY_SIZE(gmac_phy_input_parents), 0,
+ SPEAR1340_GMAC_CLK_CFG,
+ SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT,
+ SPEAR1340_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "phy_input_mclk", NULL);
+
+ clk = clk_register_aux("phy_syn_clk", "phy_syn_gclk", "phy_input_mclk",
+ 0, SPEAR1340_GMAC_CLK_SYNT, NULL, gmac_rtbl,
+ ARRAY_SIZE(gmac_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "phy_syn_clk", NULL);
+ clk_register_clkdev(clk1, "phy_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents,
+ ARRAY_SIZE(gmac_phy_parents), 0,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GMAC_PHY_CLK_SHIFT,
+ SPEAR1340_GMAC_PHY_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "stmmacphy.0", NULL);
+
+ /* clcd */
+ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents,
+ ARRAY_SIZE(clcd_synth_parents), 0,
+ SPEAR1340_CLCD_CLK_SYNT, SPEAR1340_CLCD_SYNT_CLK_SHIFT,
+ SPEAR1340_CLCD_SYNT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "clcd_syn_mclk", NULL);
+
+ clk = clk_register_frac("clcd_syn_clk", "clcd_syn_mclk", 0,
+ SPEAR1340_CLCD_CLK_SYNT, clcd_rtbl,
+ ARRAY_SIZE(clcd_rtbl), &_lock);
+ clk_register_clkdev(clk, "clcd_syn_clk", NULL);
+
+ clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents,
+ ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_CLCD_CLK_SHIFT,
+ SPEAR1340_CLCD_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "clcd_pixel_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "clcd_clk", "clcd_pixel_mclk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_CLCD_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e1000000.clcd");
+
+ /* i2s */
+ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents,
+ ARRAY_SIZE(i2s_src_parents), 0, SPEAR1340_I2S_CLK_CFG,
+ SPEAR1340_I2S_SRC_CLK_SHIFT, SPEAR1340_I2S_SRC_CLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, "i2s_src_mclk", NULL);
+
+ clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_I2S_CLK_CFG,
+ &i2s_prs1_masks, i2s_prs1_rtbl,
+ ARRAY_SIZE(i2s_prs1_rtbl), &_lock, NULL);
+ clk_register_clkdev(clk, "i2s_prs1_clk", NULL);
+
+ clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents,
+ ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT,
+ SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_REF_SHIFT,
+ SPEAR1340_I2S_REF_SEL_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "i2s_ref_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "i2s_ref_pad_clk", "i2s_ref_mclk", 0,
+ SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_I2S_REF_PAD_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, "i2s_ref_pad_clk", NULL);
+
+ clk = clk_register_aux("i2s_sclk_clk", "i2s_sclk_gclk", "i2s_ref_mclk",
+ 0, SPEAR1340_I2S_CLK_CFG, &i2s_sclk_masks,
+ i2s_sclk_rtbl, ARRAY_SIZE(i2s_sclk_rtbl), &_lock,
+ &clk1);
+ clk_register_clkdev(clk, "i2s_sclk_clk", NULL);
+ clk_register_clkdev(clk1, "i2s_sclk_gclk", NULL);
+
+ /* clock derived from ahb clk */
+ clk = clk_register_gate(NULL, "i2c0_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_I2C0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0280000.i2c");
+
+ clk = clk_register_gate(NULL, "i2c1_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_I2C1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b4000000.i2c");
+
+ clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_DMA_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "ea800000.dma");
+ clk_register_clkdev(clk, NULL, "eb000000.dma");
+
+ clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GMAC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e2000000.eth");
+
+ clk = clk_register_gate(NULL, "fsmc_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_FSMC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b0000000.flash");
+
+ clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_SMI_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "ea000000.flash");
+
+ clk = clk_register_gate(NULL, "usbh0_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_UHC0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e4000000.ohci");
+ clk_register_clkdev(clk, NULL, "e4800000.ehci");
+
+ clk = clk_register_gate(NULL, "usbh1_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_UHC1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e5000000.ohci");
+ clk_register_clkdev(clk, NULL, "e5800000.ehci");
+
+ clk = clk_register_gate(NULL, "uoc_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_UOC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e3800000.otg");
+
+ clk = clk_register_gate(NULL, "pcie_sata_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_PCIE_SATA_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "dw_pcie");
+ clk_register_clkdev(clk, NULL, "b1000000.ahci");
+
+ clk = clk_register_gate(NULL, "sysram0_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_SYSRAM0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "sysram0_clk", NULL);
+
+ clk = clk_register_gate(NULL, "sysram1_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_SYSRAM1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "sysram1_clk", NULL);
+
+ clk = clk_register_aux("adc_syn_clk", "adc_syn_gclk", "ahb_clk",
+ 0, SPEAR1340_ADC_CLK_SYNT, NULL, adc_rtbl,
+ ARRAY_SIZE(adc_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "adc_syn_clk", NULL);
+ clk_register_clkdev(clk1, "adc_syn_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "adc_clk", "adc_syn_gclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP1_CLK_ENB,
+ SPEAR1340_ADC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e0080000.adc");
+
+ /* clock derived from apb clk */
+ clk = clk_register_gate(NULL, "ssp_clk", "apb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_SSP_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0100000.spi");
+
+ clk = clk_register_gate(NULL, "gpio0_clk", "apb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPIO0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0600000.gpio");
+
+ clk = clk_register_gate(NULL, "gpio1_clk", "apb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPIO1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0680000.gpio");
+
+ clk = clk_register_gate(NULL, "i2s_play_clk", "apb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_I2S_PLAY_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b2400000.i2s-play");
+
+ clk = clk_register_gate(NULL, "i2s_rec_clk", "apb_clk", 0,
+ SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_I2S_REC_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "b2000000.i2s-rec");
+
+ clk = clk_register_gate(NULL, "kbd_clk", "apb_clk", 0,
+ SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_KBD_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0300000.kbd");
+
+ /* RAS clks */
+ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents,
+ ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1340_PLL_CFG,
+ SPEAR1340_GEN_SYNT0_1_CLK_SHIFT,
+ SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gen_syn0_1_mclk", NULL);
+
+ clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents,
+ ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1340_PLL_CFG,
+ SPEAR1340_GEN_SYNT2_3_CLK_SHIFT,
+ SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gen_syn2_3_mclk", NULL);
+
+ clk = clk_register_frac("gen_syn0_clk", "gen_syn0_1_mclk", 0,
+ SPEAR1340_GEN_CLK_SYNT0, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn0_clk", NULL);
+
+ clk = clk_register_frac("gen_syn1_clk", "gen_syn0_1_mclk", 0,
+ SPEAR1340_GEN_CLK_SYNT1, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn1_clk", NULL);
+
+ clk = clk_register_frac("gen_syn2_clk", "gen_syn2_3_mclk", 0,
+ SPEAR1340_GEN_CLK_SYNT2, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn2_clk", NULL);
+
+ clk = clk_register_frac("gen_syn3_clk", "gen_syn2_3_mclk", 0,
+ SPEAR1340_GEN_CLK_SYNT3, gen_rtbl, ARRAY_SIZE(gen_rtbl),
+ &_lock);
+ clk_register_clkdev(clk, "gen_syn3_clk", NULL);
+
+ clk = clk_register_gate(NULL, "mali_clk", "gen_syn3_clk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP3_CLK_ENB,
+ SPEAR1340_MALI_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "mali");
+
+ clk = clk_register_gate(NULL, "cec0_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CEC0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "spear_cec.0");
+
+ clk = clk_register_gate(NULL, "cec1_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CEC1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "spear_cec.1");
+
+ clk = clk_register_mux(NULL, "spdif_out_mclk", spdif_out_parents,
+ ARRAY_SIZE(spdif_out_parents), CLK_SET_RATE_PARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_OUT_CLK_SHIFT,
+ SPEAR1340_SPDIF_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "spdif_out_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "spdif_out_clk", "spdif_out_mclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP3_CLK_ENB,
+ SPEAR1340_SPDIF_OUT_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0000000.spdif-out");
+
+ clk = clk_register_mux(NULL, "spdif_in_mclk", spdif_in_parents,
+ ARRAY_SIZE(spdif_in_parents), CLK_SET_RATE_PARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_IN_CLK_SHIFT,
+ SPEAR1340_SPDIF_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "spdif_in_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "spdif_in_clk", "spdif_in_mclk",
+ CLK_SET_RATE_PARENT, SPEAR1340_PERIP3_CLK_ENB,
+ SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0100000.spdif-in");
+
+ clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "acp_clk");
+
+ clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e2800000.gpio");
+
+ clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "video_dec");
+
+ clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "video_enc");
+
+ clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "spear_vip");
+
+ clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "d0200000.cam0");
+
+ clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "d0300000.cam1");
+
+ clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "d0400000.cam2");
+
+ clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "d0500000.cam3");
+
+ clk = clk_register_gate(NULL, "pwm_clk", "ahb_clk", 0,
+ SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PWM_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "e0180000.pwm");
+}
diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c
new file mode 100644
index 000000000..1fe259021
--- /dev/null
+++ b/drivers/clk/spear/spear3xx_clock.c
@@ -0,0 +1,648 @@
+/*
+ * SPEAr3xx machines clock framework source file
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock_types.h>
+#include "clk.h"
+
+static DEFINE_SPINLOCK(_lock);
+
+#define PLL1_CTR (misc_base + 0x008)
+#define PLL1_FRQ (misc_base + 0x00C)
+#define PLL2_CTR (misc_base + 0x014)
+#define PLL2_FRQ (misc_base + 0x018)
+#define PLL_CLK_CFG (misc_base + 0x020)
+ /* PLL_CLK_CFG register masks */
+ #define MCTR_CLK_SHIFT 28
+ #define MCTR_CLK_MASK 3
+
+#define CORE_CLK_CFG (misc_base + 0x024)
+ /* CORE CLK CFG register masks */
+ #define GEN_SYNTH2_3_CLK_SHIFT 18
+ #define GEN_SYNTH2_3_CLK_MASK 1
+
+ #define HCLK_RATIO_SHIFT 10
+ #define HCLK_RATIO_MASK 2
+ #define PCLK_RATIO_SHIFT 8
+ #define PCLK_RATIO_MASK 2
+
+#define PERIP_CLK_CFG (misc_base + 0x028)
+ /* PERIP_CLK_CFG register masks */
+ #define UART_CLK_SHIFT 4
+ #define UART_CLK_MASK 1
+ #define FIRDA_CLK_SHIFT 5
+ #define FIRDA_CLK_MASK 2
+ #define GPT0_CLK_SHIFT 8
+ #define GPT1_CLK_SHIFT 11
+ #define GPT2_CLK_SHIFT 12
+ #define GPT_CLK_MASK 1
+
+#define PERIP1_CLK_ENB (misc_base + 0x02C)
+ /* PERIP1_CLK_ENB register masks */
+ #define UART_CLK_ENB 3
+ #define SSP_CLK_ENB 5
+ #define I2C_CLK_ENB 7
+ #define JPEG_CLK_ENB 8
+ #define FIRDA_CLK_ENB 10
+ #define GPT1_CLK_ENB 11
+ #define GPT2_CLK_ENB 12
+ #define ADC_CLK_ENB 15
+ #define RTC_CLK_ENB 17
+ #define GPIO_CLK_ENB 18
+ #define DMA_CLK_ENB 19
+ #define SMI_CLK_ENB 21
+ #define GMAC_CLK_ENB 23
+ #define USBD_CLK_ENB 24
+ #define USBH_CLK_ENB 25
+ #define C3_CLK_ENB 31
+
+#define RAS_CLK_ENB (misc_base + 0x034)
+ #define RAS_AHB_CLK_ENB 0
+ #define RAS_PLL1_CLK_ENB 1
+ #define RAS_APB_CLK_ENB 2
+ #define RAS_32K_CLK_ENB 3
+ #define RAS_24M_CLK_ENB 4
+ #define RAS_48M_CLK_ENB 5
+ #define RAS_PLL2_CLK_ENB 7
+ #define RAS_SYNT0_CLK_ENB 8
+ #define RAS_SYNT1_CLK_ENB 9
+ #define RAS_SYNT2_CLK_ENB 10
+ #define RAS_SYNT3_CLK_ENB 11
+
+#define PRSC0_CLK_CFG (misc_base + 0x044)
+#define PRSC1_CLK_CFG (misc_base + 0x048)
+#define PRSC2_CLK_CFG (misc_base + 0x04C)
+#define AMEM_CLK_CFG (misc_base + 0x050)
+ #define AMEM_CLK_ENB 0
+
+#define CLCD_CLK_SYNT (misc_base + 0x05C)
+#define FIRDA_CLK_SYNT (misc_base + 0x060)
+#define UART_CLK_SYNT (misc_base + 0x064)
+#define GMAC_CLK_SYNT (misc_base + 0x068)
+#define GEN0_CLK_SYNT (misc_base + 0x06C)
+#define GEN1_CLK_SYNT (misc_base + 0x070)
+#define GEN2_CLK_SYNT (misc_base + 0x074)
+#define GEN3_CLK_SYNT (misc_base + 0x078)
+
+/* pll rate configuration table, in ascending order of rates */
+static struct pll_rate_tbl pll_rtbl[] = {
+ {.mode = 0, .m = 0x53, .n = 0x0C, .p = 0x1}, /* vco 332 & pll 166 MHz */
+ {.mode = 0, .m = 0x85, .n = 0x0C, .p = 0x1}, /* vco 532 & pll 266 MHz */
+ {.mode = 0, .m = 0xA6, .n = 0x0C, .p = 0x1}, /* vco 664 & pll 332 MHz */
+};
+
+/* aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl aux_rtbl[] = {
+ /* For PLL1 = 332 MHz */
+ {.xscale = 1, .yscale = 81, .eq = 0}, /* 2.049 MHz */
+ {.xscale = 1, .yscale = 59, .eq = 0}, /* 2.822 MHz */
+ {.xscale = 2, .yscale = 81, .eq = 0}, /* 4.098 MHz */
+ {.xscale = 3, .yscale = 89, .eq = 0}, /* 5.644 MHz */
+ {.xscale = 4, .yscale = 81, .eq = 0}, /* 8.197 MHz */
+ {.xscale = 4, .yscale = 59, .eq = 0}, /* 11.254 MHz */
+ {.xscale = 2, .yscale = 27, .eq = 0}, /* 12.296 MHz */
+ {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */
+ {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */
+ {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */
+};
+
+/* gpt rate configuration table, in ascending order of rates */
+static struct gpt_rate_tbl gpt_rtbl[] = {
+ /* For pll1 = 332 MHz */
+ {.mscale = 4, .nscale = 0}, /* 41.5 MHz */
+ {.mscale = 2, .nscale = 0}, /* 55.3 MHz */
+ {.mscale = 1, .nscale = 0}, /* 83 MHz */
+};
+
+/* clock parents */
+static const char *uart0_parents[] = { "pll3_clk", "uart_syn_gclk", };
+static const char *firda_parents[] = { "pll3_clk", "firda_syn_gclk",
+};
+static const char *gpt0_parents[] = { "pll3_clk", "gpt0_syn_clk", };
+static const char *gpt1_parents[] = { "pll3_clk", "gpt1_syn_clk", };
+static const char *gpt2_parents[] = { "pll3_clk", "gpt2_syn_clk", };
+static const char *gen2_3_parents[] = { "pll1_clk", "pll2_clk", };
+static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none",
+ "pll2_clk", };
+
+#ifdef CONFIG_MACH_SPEAR300
+static void __init spear300_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_clk", 0,
+ 1, 1);
+ clk_register_clkdev(clk, NULL, "60000000.clcd");
+
+ clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "94000000.flash");
+
+ clk = clk_register_fixed_factor(NULL, "sdhci_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "70000000.sdhci");
+
+ clk = clk_register_fixed_factor(NULL, "gpio1_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a9000000.gpio");
+
+ clk = clk_register_fixed_factor(NULL, "kbd_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a0000000.kbd");
+}
+#else
+static inline void spear300_clk_init(void) { }
+#endif
+
+/* array of all spear 310 clock lookups */
+#ifdef CONFIG_MACH_SPEAR310
+static void __init spear310_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "emi", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "44000000.flash");
+
+ clk = clk_register_fixed_factor(NULL, "tdm_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "tdm");
+
+ clk = clk_register_fixed_factor(NULL, "uart1_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "b2000000.serial");
+
+ clk = clk_register_fixed_factor(NULL, "uart2_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "b2080000.serial");
+
+ clk = clk_register_fixed_factor(NULL, "uart3_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "b2100000.serial");
+
+ clk = clk_register_fixed_factor(NULL, "uart4_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "b2180000.serial");
+
+ clk = clk_register_fixed_factor(NULL, "uart5_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "b2200000.serial");
+}
+#else
+static inline void spear310_clk_init(void) { }
+#endif
+
+/* array of all spear 320 clock lookups */
+#ifdef CONFIG_MACH_SPEAR320
+
+#define SPEAR320_CONTROL_REG (soc_config_base + 0x0010)
+#define SPEAR320_EXT_CTRL_REG (soc_config_base + 0x0018)
+
+ #define SPEAR320_UARTX_PCLK_MASK 0x1
+ #define SPEAR320_UART2_PCLK_SHIFT 8
+ #define SPEAR320_UART3_PCLK_SHIFT 9
+ #define SPEAR320_UART4_PCLK_SHIFT 10
+ #define SPEAR320_UART5_PCLK_SHIFT 11
+ #define SPEAR320_UART6_PCLK_SHIFT 12
+ #define SPEAR320_RS485_PCLK_SHIFT 13
+ #define SMII_PCLK_SHIFT 18
+ #define SMII_PCLK_MASK 2
+ #define SMII_PCLK_VAL_PAD 0x0
+ #define SMII_PCLK_VAL_PLL2 0x1
+ #define SMII_PCLK_VAL_SYNTH0 0x2
+ #define SDHCI_PCLK_SHIFT 15
+ #define SDHCI_PCLK_MASK 1
+ #define SDHCI_PCLK_VAL_48M 0x0
+ #define SDHCI_PCLK_VAL_SYNTH3 0x1
+ #define I2S_REF_PCLK_SHIFT 8
+ #define I2S_REF_PCLK_MASK 1
+ #define I2S_REF_PCLK_SYNTH_VAL 0x1
+ #define I2S_REF_PCLK_PLL2_VAL 0x0
+ #define UART1_PCLK_SHIFT 6
+ #define UART1_PCLK_MASK 1
+ #define SPEAR320_UARTX_PCLK_VAL_SYNTH1 0x0
+ #define SPEAR320_UARTX_PCLK_VAL_APB 0x1
+
+static const char *i2s_ref_parents[] = { "ras_pll2_clk", "ras_syn2_gclk", };
+static const char *sdhci_parents[] = { "ras_pll3_clk", "ras_syn3_gclk", };
+static const char *smii0_parents[] = { "smii_125m_pad", "ras_pll2_clk",
+ "ras_syn0_gclk", };
+static const char *uartx_parents[] = { "ras_syn1_gclk", "ras_apb_clk", };
+
+static void __init spear320_clk_init(void __iomem *soc_config_base)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(NULL, "smii_125m_pad_clk", NULL,
+ CLK_IS_ROOT, 125000000);
+ clk_register_clkdev(clk, "smii_125m_pad", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_clk", 0,
+ 1, 1);
+ clk_register_clkdev(clk, NULL, "90000000.clcd");
+
+ clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "emi", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "4c000000.flash");
+
+ clk = clk_register_fixed_factor(NULL, "i2c1_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a7000000.i2c");
+
+ clk = clk_register_fixed_factor(NULL, "pwm_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a8000000.pwm");
+
+ clk = clk_register_fixed_factor(NULL, "ssp1_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a5000000.spi");
+
+ clk = clk_register_fixed_factor(NULL, "ssp2_clk", "ras_ahb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a6000000.spi");
+
+ clk = clk_register_fixed_factor(NULL, "can0_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "c_can_platform.0");
+
+ clk = clk_register_fixed_factor(NULL, "can1_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "c_can_platform.1");
+
+ clk = clk_register_fixed_factor(NULL, "i2s_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "a9400000.i2s");
+
+ clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents,
+ ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_CONTROL_REG, I2S_REF_PCLK_SHIFT,
+ I2S_REF_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "i2s_ref_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "i2s_sclk", "i2s_ref_clk",
+ CLK_SET_RATE_PARENT, 1,
+ 4);
+ clk_register_clkdev(clk, "i2s_sclk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "macb1_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "hclk", "aa000000.eth");
+
+ clk = clk_register_fixed_factor(NULL, "macb2_clk", "ras_apb_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "hclk", "ab000000.eth");
+
+ clk = clk_register_mux(NULL, "rs485_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_EXT_CTRL_REG, SPEAR320_RS485_PCLK_SHIFT,
+ SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "a9300000.serial");
+
+ clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents,
+ ARRAY_SIZE(sdhci_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_CONTROL_REG, SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "70000000.sdhci");
+
+ clk = clk_register_mux(NULL, "smii_pclk", smii0_parents,
+ ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG,
+ SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "smii_pclk");
+
+ clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1);
+ clk_register_clkdev(clk, NULL, "smii");
+
+ clk = clk_register_mux(NULL, "uart1_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_CONTROL_REG, UART1_PCLK_SHIFT, UART1_PCLK_MASK,
+ 0, &_lock);
+ clk_register_clkdev(clk, NULL, "a3000000.serial");
+
+ clk = clk_register_mux(NULL, "uart2_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_EXT_CTRL_REG, SPEAR320_UART2_PCLK_SHIFT,
+ SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "a4000000.serial");
+
+ clk = clk_register_mux(NULL, "uart3_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_EXT_CTRL_REG, SPEAR320_UART3_PCLK_SHIFT,
+ SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "a9100000.serial");
+
+ clk = clk_register_mux(NULL, "uart4_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_EXT_CTRL_REG, SPEAR320_UART4_PCLK_SHIFT,
+ SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "a9200000.serial");
+
+ clk = clk_register_mux(NULL, "uart5_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_EXT_CTRL_REG, SPEAR320_UART5_PCLK_SHIFT,
+ SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "60000000.serial");
+
+ clk = clk_register_mux(NULL, "uart6_clk", uartx_parents,
+ ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ SPEAR320_EXT_CTRL_REG, SPEAR320_UART6_PCLK_SHIFT,
+ SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "60100000.serial");
+}
+#else
+static inline void spear320_clk_init(void __iomem *soc_config_base) { }
+#endif
+
+void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_base)
+{
+ struct clk *clk, *clk1;
+
+ clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT,
+ 32000);
+ clk_register_clkdev(clk, "osc_32k_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "osc_24m_clk", NULL, CLK_IS_ROOT,
+ 24000000);
+ clk_register_clkdev(clk, "osc_24m_clk", NULL);
+
+ /* clock derived from 32 KHz osc clk */
+ clk = clk_register_gate(NULL, "rtc-spear", "osc_32k_clk", 0,
+ PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc900000.rtc");
+
+ /* clock derived from 24 MHz osc clk */
+ clk = clk_register_fixed_rate(NULL, "pll3_clk", "osc_24m_clk", 0,
+ 48000000);
+ clk_register_clkdev(clk, "pll3_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_24m_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "fc880000.wdt");
+
+ clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL,
+ "osc_24m_clk", 0, PLL1_CTR, PLL1_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco1_clk", NULL);
+ clk_register_clkdev(clk1, "pll1_clk", NULL);
+
+ clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL,
+ "osc_24m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl,
+ ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco2_clk", NULL);
+ clk_register_clkdev(clk1, "pll2_clk", NULL);
+
+ /* clock derived from pll1 clk */
+ clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "cpu_clk", NULL);
+
+ clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk",
+ CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT,
+ HCLK_RATIO_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "ahb_clk", NULL);
+
+ clk = clk_register_aux("uart_syn_clk", "uart_syn_gclk", "pll1_clk", 0,
+ UART_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "uart_syn_clk", NULL);
+ clk_register_clkdev(clk1, "uart_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents,
+ ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT,
+ PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "uart0_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart0", "uart0_mclk",
+ CLK_SET_RATE_PARENT, PERIP1_CLK_ENB, UART_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "d0000000.serial");
+
+ clk = clk_register_aux("firda_syn_clk", "firda_syn_gclk", "pll1_clk", 0,
+ FIRDA_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "firda_syn_clk", NULL);
+ clk_register_clkdev(clk1, "firda_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "firda_mclk", firda_parents,
+ ARRAY_SIZE(firda_parents), CLK_SET_RATE_PARENT,
+ PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "firda_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "firda_clk", "firda_mclk",
+ CLK_SET_RATE_PARENT, PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "firda");
+
+ /* gpt clocks */
+ clk_register_gpt("gpt0_syn_clk", "pll1_clk", 0, PRSC0_CLK_CFG, gpt_rtbl,
+ ARRAY_SIZE(gpt_rtbl), &_lock);
+ clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents,
+ ARRAY_SIZE(gpt0_parents), CLK_SET_RATE_PARENT,
+ PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "gpt0");
+
+ clk_register_gpt("gpt1_syn_clk", "pll1_clk", 0, PRSC1_CLK_CFG, gpt_rtbl,
+ ARRAY_SIZE(gpt_rtbl), &_lock);
+ clk = clk_register_mux(NULL, "gpt1_mclk", gpt1_parents,
+ ARRAY_SIZE(gpt1_parents), CLK_SET_RATE_PARENT,
+ PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gpt1_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk",
+ CLK_SET_RATE_PARENT, PERIP1_CLK_ENB, GPT1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt1");
+
+ clk_register_gpt("gpt2_syn_clk", "pll1_clk", 0, PRSC2_CLK_CFG, gpt_rtbl,
+ ARRAY_SIZE(gpt_rtbl), &_lock);
+ clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents,
+ ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_PARENT,
+ PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gpt2_mclk", NULL);
+ clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk",
+ CLK_SET_RATE_PARENT, PERIP1_CLK_ENB, GPT2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, NULL, "gpt2");
+
+ /* general synths clocks */
+ clk = clk_register_aux("gen0_syn_clk", "gen0_syn_gclk", "pll1_clk",
+ 0, GEN0_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "gen0_syn_clk", NULL);
+ clk_register_clkdev(clk1, "gen0_syn_gclk", NULL);
+
+ clk = clk_register_aux("gen1_syn_clk", "gen1_syn_gclk", "pll1_clk",
+ 0, GEN1_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "gen1_syn_clk", NULL);
+ clk_register_clkdev(clk1, "gen1_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "gen2_3_par_clk", gen2_3_parents,
+ ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG,
+ GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0,
+ &_lock);
+ clk_register_clkdev(clk, "gen2_3_par_clk", NULL);
+
+ clk = clk_register_aux("gen2_syn_clk", "gen2_syn_gclk",
+ "gen2_3_par_clk", 0, GEN2_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "gen2_syn_clk", NULL);
+ clk_register_clkdev(clk1, "gen2_syn_gclk", NULL);
+
+ clk = clk_register_aux("gen3_syn_clk", "gen3_syn_gclk",
+ "gen2_3_par_clk", 0, GEN3_CLK_SYNT, NULL, aux_rtbl,
+ ARRAY_SIZE(aux_rtbl), &_lock, &clk1);
+ clk_register_clkdev(clk, "gen3_syn_clk", NULL);
+ clk_register_clkdev(clk1, "gen3_syn_gclk", NULL);
+
+ /* clock derived from pll3 clk */
+ clk = clk_register_gate(NULL, "usbh_clk", "pll3_clk", 0, PERIP1_CLK_ENB,
+ USBH_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e1800000.ehci");
+ clk_register_clkdev(clk, NULL, "e1900000.ohci");
+ clk_register_clkdev(clk, NULL, "e2100000.ohci");
+
+ clk = clk_register_fixed_factor(NULL, "usbh.0_clk", "usbh_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "usbh.0_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "usbh.1_clk", "usbh_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, "usbh.1_clk", NULL);
+
+ clk = clk_register_gate(NULL, "usbd_clk", "pll3_clk", 0, PERIP1_CLK_ENB,
+ USBD_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e1100000.usbd");
+
+ /* clock derived from ahb clk */
+ clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2,
+ 1);
+ clk_register_clkdev(clk, "ahbmult2_clk", NULL);
+
+ clk = clk_register_mux(NULL, "ddr_clk", ddr_parents,
+ ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT,
+ MCTR_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "ddr_clk", NULL);
+
+ clk = clk_register_divider(NULL, "apb_clk", "ahb_clk",
+ CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT,
+ PCLK_RATIO_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "apb_clk", NULL);
+
+ clk = clk_register_gate(NULL, "amem_clk", "ahb_clk", 0, AMEM_CLK_CFG,
+ AMEM_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "amem_clk", NULL);
+
+ clk = clk_register_gate(NULL, "c3_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ C3_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "c3_clk");
+
+ clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ DMA_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc400000.dma");
+
+ clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ GMAC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e0800000.eth");
+
+ clk = clk_register_gate(NULL, "i2c0_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ I2C_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0180000.i2c");
+
+ clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ JPEG_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "jpeg");
+
+ clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ SMI_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc000000.flash");
+
+ /* clock derived from apb clk */
+ clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ ADC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0080000.adc");
+
+ clk = clk_register_gate(NULL, "gpio0_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ GPIO_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc980000.gpio");
+
+ clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ SSP_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0100000.spi");
+
+ /* RAS clk enable */
+ clk = clk_register_gate(NULL, "ras_ahb_clk", "ahb_clk", 0, RAS_CLK_ENB,
+ RAS_AHB_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_ahb_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_apb_clk", "apb_clk", 0, RAS_CLK_ENB,
+ RAS_APB_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_apb_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_32k_clk", "osc_32k_clk", 0,
+ RAS_CLK_ENB, RAS_32K_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_32k_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_24m_clk", "osc_24m_clk", 0,
+ RAS_CLK_ENB, RAS_24M_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_24m_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_pll1_clk", "pll1_clk", 0,
+ RAS_CLK_ENB, RAS_PLL1_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_pll1_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_pll2_clk", "pll2_clk", 0,
+ RAS_CLK_ENB, RAS_PLL2_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_pll2_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_pll3_clk", "pll3_clk", 0,
+ RAS_CLK_ENB, RAS_48M_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, "ras_pll3_clk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_syn0_gclk", "gen0_syn_gclk",
+ CLK_SET_RATE_PARENT, RAS_CLK_ENB, RAS_SYNT0_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_syn0_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_syn1_gclk", "gen1_syn_gclk",
+ CLK_SET_RATE_PARENT, RAS_CLK_ENB, RAS_SYNT1_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_syn1_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_syn2_gclk", "gen2_syn_gclk",
+ CLK_SET_RATE_PARENT, RAS_CLK_ENB, RAS_SYNT2_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_syn2_gclk", NULL);
+
+ clk = clk_register_gate(NULL, "ras_syn3_gclk", "gen3_syn_gclk",
+ CLK_SET_RATE_PARENT, RAS_CLK_ENB, RAS_SYNT3_CLK_ENB, 0,
+ &_lock);
+ clk_register_clkdev(clk, "ras_syn3_gclk", NULL);
+
+ if (of_machine_is_compatible("st,spear300"))
+ spear300_clk_init();
+ else if (of_machine_is_compatible("st,spear310"))
+ spear310_clk_init();
+ else if (of_machine_is_compatible("st,spear320"))
+ spear320_clk_init(soc_config_base);
+}
diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c
new file mode 100644
index 000000000..9406f2426
--- /dev/null
+++ b/drivers/clk/spear/spear6xx_clock.c
@@ -0,0 +1,340 @@
+/*
+ * SPEAr6xx machines clock framework source file
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/spinlock_types.h>
+#include "clk.h"
+
+static DEFINE_SPINLOCK(_lock);
+
+#define PLL1_CTR (misc_base + 0x008)
+#define PLL1_FRQ (misc_base + 0x00C)
+#define PLL2_CTR (misc_base + 0x014)
+#define PLL2_FRQ (misc_base + 0x018)
+#define PLL_CLK_CFG (misc_base + 0x020)
+ /* PLL_CLK_CFG register masks */
+ #define MCTR_CLK_SHIFT 28
+ #define MCTR_CLK_MASK 3
+
+#define CORE_CLK_CFG (misc_base + 0x024)
+ /* CORE CLK CFG register masks */
+ #define HCLK_RATIO_SHIFT 10
+ #define HCLK_RATIO_MASK 2
+ #define PCLK_RATIO_SHIFT 8
+ #define PCLK_RATIO_MASK 2
+
+#define PERIP_CLK_CFG (misc_base + 0x028)
+ /* PERIP_CLK_CFG register masks */
+ #define CLCD_CLK_SHIFT 2
+ #define CLCD_CLK_MASK 2
+ #define UART_CLK_SHIFT 4
+ #define UART_CLK_MASK 1
+ #define FIRDA_CLK_SHIFT 5
+ #define FIRDA_CLK_MASK 2
+ #define GPT0_CLK_SHIFT 8
+ #define GPT1_CLK_SHIFT 10
+ #define GPT2_CLK_SHIFT 11
+ #define GPT3_CLK_SHIFT 12
+ #define GPT_CLK_MASK 1
+
+#define PERIP1_CLK_ENB (misc_base + 0x02C)
+ /* PERIP1_CLK_ENB register masks */
+ #define UART0_CLK_ENB 3
+ #define UART1_CLK_ENB 4
+ #define SSP0_CLK_ENB 5
+ #define SSP1_CLK_ENB 6
+ #define I2C_CLK_ENB 7
+ #define JPEG_CLK_ENB 8
+ #define FSMC_CLK_ENB 9
+ #define FIRDA_CLK_ENB 10
+ #define GPT2_CLK_ENB 11
+ #define GPT3_CLK_ENB 12
+ #define GPIO2_CLK_ENB 13
+ #define SSP2_CLK_ENB 14
+ #define ADC_CLK_ENB 15
+ #define GPT1_CLK_ENB 11
+ #define RTC_CLK_ENB 17
+ #define GPIO1_CLK_ENB 18
+ #define DMA_CLK_ENB 19
+ #define SMI_CLK_ENB 21
+ #define CLCD_CLK_ENB 22
+ #define GMAC_CLK_ENB 23
+ #define USBD_CLK_ENB 24
+ #define USBH0_CLK_ENB 25
+ #define USBH1_CLK_ENB 26
+
+#define PRSC0_CLK_CFG (misc_base + 0x044)
+#define PRSC1_CLK_CFG (misc_base + 0x048)
+#define PRSC2_CLK_CFG (misc_base + 0x04C)
+
+#define CLCD_CLK_SYNT (misc_base + 0x05C)
+#define FIRDA_CLK_SYNT (misc_base + 0x060)
+#define UART_CLK_SYNT (misc_base + 0x064)
+
+/* vco rate configuration table, in ascending order of rates */
+static struct pll_rate_tbl pll_rtbl[] = {
+ {.mode = 0, .m = 0x53, .n = 0x0F, .p = 0x1}, /* vco 332 & pll 166 MHz */
+ {.mode = 0, .m = 0x85, .n = 0x0F, .p = 0x1}, /* vco 532 & pll 266 MHz */
+ {.mode = 0, .m = 0xA6, .n = 0x0F, .p = 0x1}, /* vco 664 & pll 332 MHz */
+};
+
+/* aux rate configuration table, in ascending order of rates */
+static struct aux_rate_tbl aux_rtbl[] = {
+ /* For PLL1 = 332 MHz */
+ {.xscale = 2, .yscale = 27, .eq = 0}, /* 12.296 MHz */
+ {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */
+ {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */
+ {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */
+};
+
+static const char *clcd_parents[] = { "pll3_clk", "clcd_syn_gclk", };
+static const char *firda_parents[] = { "pll3_clk", "firda_syn_gclk", };
+static const char *uart_parents[] = { "pll3_clk", "uart_syn_gclk", };
+static const char *gpt0_1_parents[] = { "pll3_clk", "gpt0_1_syn_clk", };
+static const char *gpt2_parents[] = { "pll3_clk", "gpt2_syn_clk", };
+static const char *gpt3_parents[] = { "pll3_clk", "gpt3_syn_clk", };
+static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none",
+ "pll2_clk", };
+
+/* gpt rate configuration table, in ascending order of rates */
+static struct gpt_rate_tbl gpt_rtbl[] = {
+ /* For pll1 = 332 MHz */
+ {.mscale = 4, .nscale = 0}, /* 41.5 MHz */
+ {.mscale = 2, .nscale = 0}, /* 55.3 MHz */
+ {.mscale = 1, .nscale = 0}, /* 83 MHz */
+};
+
+void __init spear6xx_clk_init(void __iomem *misc_base)
+{
+ struct clk *clk, *clk1;
+
+ clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT,
+ 32000);
+ clk_register_clkdev(clk, "osc_32k_clk", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "osc_30m_clk", NULL, CLK_IS_ROOT,
+ 30000000);
+ clk_register_clkdev(clk, "osc_30m_clk", NULL);
+
+ /* clock derived from 32 KHz osc clk */
+ clk = clk_register_gate(NULL, "rtc_spear", "osc_32k_clk", 0,
+ PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "rtc-spear");
+
+ /* clock derived from 30 MHz osc clk */
+ clk = clk_register_fixed_rate(NULL, "pll3_clk", "osc_24m_clk", 0,
+ 48000000);
+ clk_register_clkdev(clk, "pll3_clk", NULL);
+
+ clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "osc_30m_clk",
+ 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, ARRAY_SIZE(pll_rtbl),
+ &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco1_clk", NULL);
+ clk_register_clkdev(clk1, "pll1_clk", NULL);
+
+ clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "osc_30m_clk",
+ 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, ARRAY_SIZE(pll_rtbl),
+ &_lock, &clk1, NULL);
+ clk_register_clkdev(clk, "vco2_clk", NULL);
+ clk_register_clkdev(clk1, "pll2_clk", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_30m_clk", 0, 1,
+ 1);
+ clk_register_clkdev(clk, NULL, "wdt");
+
+ /* clock derived from pll1 clk */
+ clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "cpu_clk", NULL);
+
+ clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk",
+ CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT,
+ HCLK_RATIO_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "ahb_clk", NULL);
+
+ clk = clk_register_aux("uart_syn_clk", "uart_syn_gclk", "pll1_clk", 0,
+ UART_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "uart_syn_clk", NULL);
+ clk_register_clkdev(clk1, "uart_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "uart_mclk", uart_parents,
+ ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG,
+ UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "uart_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "uart0", "uart_mclk", 0, PERIP1_CLK_ENB,
+ UART0_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0000000.serial");
+
+ clk = clk_register_gate(NULL, "uart1", "uart_mclk", 0, PERIP1_CLK_ENB,
+ UART1_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0080000.serial");
+
+ clk = clk_register_aux("firda_syn_clk", "firda_syn_gclk", "pll1_clk",
+ 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "firda_syn_clk", NULL);
+ clk_register_clkdev(clk1, "firda_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "firda_mclk", firda_parents,
+ ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG,
+ FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "firda_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "firda_clk", "firda_mclk", 0,
+ PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "firda");
+
+ clk = clk_register_aux("clcd_syn_clk", "clcd_syn_gclk", "pll1_clk",
+ 0, CLCD_CLK_SYNT, NULL, aux_rtbl, ARRAY_SIZE(aux_rtbl),
+ &_lock, &clk1);
+ clk_register_clkdev(clk, "clcd_syn_clk", NULL);
+ clk_register_clkdev(clk1, "clcd_syn_gclk", NULL);
+
+ clk = clk_register_mux(NULL, "clcd_mclk", clcd_parents,
+ ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG,
+ CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "clcd_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "clcd_clk", "clcd_mclk", 0,
+ PERIP1_CLK_ENB, CLCD_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "clcd");
+
+ /* gpt clocks */
+ clk = clk_register_gpt("gpt0_1_syn_clk", "pll1_clk", 0, PRSC0_CLK_CFG,
+ gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock);
+ clk_register_clkdev(clk, "gpt0_1_syn_clk", NULL);
+
+ clk = clk_register_mux(NULL, "gpt0_mclk", gpt0_1_parents,
+ ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG,
+ GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "gpt0");
+
+ clk = clk_register_mux(NULL, "gpt1_mclk", gpt0_1_parents,
+ ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG,
+ GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gpt1_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0,
+ PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "gpt1");
+
+ clk = clk_register_gpt("gpt2_syn_clk", "pll1_clk", 0, PRSC1_CLK_CFG,
+ gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock);
+ clk_register_clkdev(clk, "gpt2_syn_clk", NULL);
+
+ clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents,
+ ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG,
+ GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gpt2_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0,
+ PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "gpt2");
+
+ clk = clk_register_gpt("gpt3_syn_clk", "pll1_clk", 0, PRSC2_CLK_CFG,
+ gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock);
+ clk_register_clkdev(clk, "gpt3_syn_clk", NULL);
+
+ clk = clk_register_mux(NULL, "gpt3_mclk", gpt3_parents,
+ ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG,
+ GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "gpt3_mclk", NULL);
+
+ clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0,
+ PERIP1_CLK_ENB, GPT3_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "gpt3");
+
+ /* clock derived from pll3 clk */
+ clk = clk_register_gate(NULL, "usbh0_clk", "pll3_clk", 0,
+ PERIP1_CLK_ENB, USBH0_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e1800000.ehci");
+ clk_register_clkdev(clk, NULL, "e1900000.ohci");
+
+ clk = clk_register_gate(NULL, "usbh1_clk", "pll3_clk", 0,
+ PERIP1_CLK_ENB, USBH1_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e2000000.ehci");
+ clk_register_clkdev(clk, NULL, "e2100000.ohci");
+
+ clk = clk_register_gate(NULL, "usbd_clk", "pll3_clk", 0, PERIP1_CLK_ENB,
+ USBD_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "designware_udc");
+
+ /* clock derived from ahb clk */
+ clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2,
+ 1);
+ clk_register_clkdev(clk, "ahbmult2_clk", NULL);
+
+ clk = clk_register_mux(NULL, "ddr_clk", ddr_parents,
+ ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT,
+ MCTR_CLK_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "ddr_clk", NULL);
+
+ clk = clk_register_divider(NULL, "apb_clk", "ahb_clk",
+ CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT,
+ PCLK_RATIO_MASK, 0, &_lock);
+ clk_register_clkdev(clk, "apb_clk", NULL);
+
+ clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ DMA_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc400000.dma");
+
+ clk = clk_register_gate(NULL, "fsmc_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ FSMC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d1800000.flash");
+
+ clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ GMAC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "e0800000.ethernet");
+
+ clk = clk_register_gate(NULL, "i2c_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ I2C_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d0200000.i2c");
+
+ clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ JPEG_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "jpeg");
+
+ clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB,
+ SMI_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc000000.flash");
+
+ /* clock derived from apb clk */
+ clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ ADC_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "adc");
+
+ clk = clk_register_fixed_factor(NULL, "gpio0_clk", "apb_clk", 0, 1, 1);
+ clk_register_clkdev(clk, NULL, "f0100000.gpio");
+
+ clk = clk_register_gate(NULL, "gpio1_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ GPIO1_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "fc980000.gpio");
+
+ clk = clk_register_gate(NULL, "gpio2_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ GPIO2_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "d8100000.gpio");
+
+ clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ SSP0_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "ssp-pl022.0");
+
+ clk = clk_register_gate(NULL, "ssp1_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ SSP1_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "ssp-pl022.1");
+
+ clk = clk_register_gate(NULL, "ssp2_clk", "apb_clk", 0, PERIP1_CLK_ENB,
+ SSP2_CLK_ENB, 0, &_lock);
+ clk_register_clkdev(clk, NULL, "ssp-pl022.2");
+}
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644
index 000000000..b5bac9176
--- /dev/null
+++ b/drivers/clk/sunxi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
new file mode 100644
index 000000000..88523f91d
--- /dev/null
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2013 Emilio López <emilio@elopez.com.ar>
+ *
+ * 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.
+ *
+ * Adjustable factor-based clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+#include <linux/delay.h>
+
+#include "clk-factors.h"
+
+/*
+ * DOC: basic adjustable factor-based clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable.
+ * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+struct clk_factors {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct clk_factors_config *config;
+ void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
+ spinlock_t *lock;
+};
+
+#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+
+#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos))
+#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
+#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
+
+#define FACTOR_SET(bit, len, reg, val) \
+ (((reg) & CLRMASK(len, bit)) | (val << (bit)))
+
+static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u8 n = 1, k = 0, p = 0, m = 0;
+ u32 reg;
+ unsigned long rate;
+ struct clk_factors *factors = to_clk_factors(hw);
+ struct clk_factors_config *config = factors->config;
+
+ /* Fetch the register value */
+ reg = readl(factors->reg);
+
+ /* Get each individual factor if applicable */
+ if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+ n = FACTOR_GET(config->nshift, config->nwidth, reg);
+ if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+ k = FACTOR_GET(config->kshift, config->kwidth, reg);
+ if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+ m = FACTOR_GET(config->mshift, config->mwidth, reg);
+ if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+ p = FACTOR_GET(config->pshift, config->pwidth, reg);
+
+ /* Calculate the rate */
+ rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+
+ return rate;
+}
+
+static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_factors *factors = to_clk_factors(hw);
+ factors->get_factors((u32 *)&rate, (u32)*parent_rate,
+ NULL, NULL, NULL, NULL);
+
+ return rate;
+}
+
+static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ u8 n, k, m, p;
+ u32 reg;
+ struct clk_factors *factors = to_clk_factors(hw);
+ struct clk_factors_config *config = factors->config;
+ unsigned long flags = 0;
+
+ factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
+
+ if (factors->lock)
+ spin_lock_irqsave(factors->lock, flags);
+
+ /* Fetch the register value */
+ reg = readl(factors->reg);
+
+ /* Set up the new factors - macros do not do anything if width is 0 */
+ reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
+ reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
+ reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
+ reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
+
+ /* Apply them now */
+ writel(reg, factors->reg);
+
+ /* delay 500us so pll stabilizes */
+ __delay((rate >> 20) * 500 / 2);
+
+ if (factors->lock)
+ spin_unlock_irqrestore(factors->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops clk_factors_ops = {
+ .recalc_rate = clk_factors_recalc_rate,
+ .round_rate = clk_factors_round_rate,
+ .set_rate = clk_factors_set_rate,
+};
+
+/**
+ * clk_register_factors - register a factors clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust factors
+ * @config: shift and width of factors n, k, m and p
+ * @get_factors: function to calculate the factors for a given frequency
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_factors(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags, void __iomem *reg,
+ struct clk_factors_config *config,
+ void (*get_factors)(u32 *rate, u32 parent,
+ u8 *n, u8 *k, u8 *m, u8 *p),
+ spinlock_t *lock)
+{
+ struct clk_factors *factors;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the factors */
+ factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
+ if (!factors) {
+ pr_err("%s: could not allocate factors clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &clk_factors_ops;
+ init.flags = flags;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* struct clk_factors assignments */
+ factors->reg = reg;
+ factors->config = config;
+ factors->lock = lock;
+ factors->hw.init = &init;
+ factors->get_factors = get_factors;
+
+ /* register the clock */
+ clk = clk_register(dev, &factors->hw);
+
+ if (IS_ERR(clk))
+ kfree(factors);
+
+ return clk;
+}
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
new file mode 100644
index 000000000..f49851cc4
--- /dev/null
+++ b/drivers/clk/sunxi/clk-factors.h
@@ -0,0 +1,27 @@
+#ifndef __MACH_SUNXI_CLK_FACTORS_H
+#define __MACH_SUNXI_CLK_FACTORS_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#define SUNXI_FACTORS_NOT_APPLICABLE (0)
+
+struct clk_factors_config {
+ u8 nshift;
+ u8 nwidth;
+ u8 kshift;
+ u8 kwidth;
+ u8 mshift;
+ u8 mwidth;
+ u8 pshift;
+ u8 pwidth;
+};
+
+struct clk *clk_register_factors(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags, void __iomem *reg,
+ struct clk_factors_config *config,
+ void (*get_factors) (u32 *rate, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p),
+ spinlock_t *lock);
+#endif
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
new file mode 100644
index 000000000..8492ad1d5
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * 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.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/sunxi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk-factors.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+/**
+ * sunxi_osc_clk_setup() - Setup function for gatable oscillator
+ */
+
+#define SUNXI_OSC24M_GATE 0
+
+static void __init sunxi_osc_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ struct clk_fixed_rate *fixed;
+ struct clk_gate *gate;
+ const char *clk_name = node->name;
+ u32 rate;
+
+ /* allocate fixed-rate and gate clock structs */
+ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+ if (!fixed)
+ return;
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(fixed);
+ return;
+ }
+
+ if (of_property_read_u32(node, "clock-frequency", &rate))
+ return;
+
+ /* set up gate and fixed rate properties */
+ gate->reg = of_iomap(node, 0);
+ gate->bit_idx = SUNXI_OSC24M_GATE;
+ gate->lock = &clk_lock;
+ fixed->fixed_rate = rate;
+
+ clk = clk_register_composite(NULL, clk_name,
+ NULL, 0,
+ NULL, NULL,
+ &fixed->hw, &clk_fixed_rate_ops,
+ &gate->hw, &clk_gate_ops,
+ CLK_IS_ROOT);
+
+ if (clk) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+ }
+}
+
+
+
+/**
+ * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
+ * PLL1 rate is calculated as follows
+ * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+ * parent_rate is always 24Mhz
+ */
+
+static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p)
+{
+ u8 div;
+
+ /* Normalize value to a 6M multiple */
+ div = *freq / 6000000;
+ *freq = 6000000 * div;
+
+ /* we were called to round the frequency, we can now return */
+ if (n == NULL)
+ return;
+
+ /* m is always zero for pll1 */
+ *m = 0;
+
+ /* k is 1 only on these cases */
+ if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
+ *k = 1;
+ else
+ *k = 0;
+
+ /* p will be 3 for divs under 10 */
+ if (div < 10)
+ *p = 3;
+
+ /* p will be 2 for divs between 10 - 20 and odd divs under 32 */
+ else if (div < 20 || (div < 32 && (div & 1)))
+ *p = 2;
+
+ /* p will be 1 for even divs under 32, divs under 40 and odd pairs
+ * of divs between 40-62 */
+ else if (div < 40 || (div < 64 && (div & 2)))
+ *p = 1;
+
+ /* any other entries have p = 0 */
+ else
+ *p = 0;
+
+ /* calculate a suitable n based on k and p */
+ div <<= *p;
+ div /= (*k + 1);
+ *n = div / 4;
+}
+
+
+
+/**
+ * sunxi_get_apb1_factors() - calculates m, p factors for APB1
+ * APB1 rate is calculated as follows
+ * rate = (parent_rate >> p) / (m + 1);
+ */
+
+static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p)
+{
+ u8 calcm, calcp;
+
+ if (parent_rate < *freq)
+ *freq = parent_rate;
+
+ parent_rate = (parent_rate + (*freq - 1)) / *freq;
+
+ /* Invalid rate! */
+ if (parent_rate > 32)
+ return;
+
+ if (parent_rate <= 4)
+ calcp = 0;
+ else if (parent_rate <= 8)
+ calcp = 1;
+ else if (parent_rate <= 16)
+ calcp = 2;
+ else
+ calcp = 3;
+
+ calcm = (parent_rate >> calcp) - 1;
+
+ *freq = (parent_rate >> calcp) / (calcm + 1);
+
+ /* we were called to round the frequency, we can now return */
+ if (n == NULL)
+ return;
+
+ *m = calcm;
+ *p = calcp;
+}
+
+
+
+/**
+ * sunxi_factors_clk_setup() - Setup function for factor clocks
+ */
+
+struct factors_data {
+ struct clk_factors_config *table;
+ void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+};
+
+static struct clk_factors_config pll1_config = {
+ .nshift = 8,
+ .nwidth = 5,
+ .kshift = 4,
+ .kwidth = 2,
+ .mshift = 0,
+ .mwidth = 2,
+ .pshift = 16,
+ .pwidth = 2,
+};
+
+static struct clk_factors_config apb1_config = {
+ .mshift = 0,
+ .mwidth = 5,
+ .pshift = 16,
+ .pwidth = 2,
+};
+
+static const __initconst struct factors_data pll1_data = {
+ .table = &pll1_config,
+ .getter = sunxi_get_pll1_factors,
+};
+
+static const __initconst struct factors_data apb1_data = {
+ .table = &apb1_config,
+ .getter = sunxi_get_apb1_factors,
+};
+
+static void __init sunxi_factors_clk_setup(struct device_node *node,
+ struct factors_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parent;
+ void *reg;
+
+ reg = of_iomap(node, 0);
+
+ parent = of_clk_get_parent_name(node, 0);
+
+ clk = clk_register_factors(NULL, clk_name, parent, 0, reg,
+ data->table, data->getter, &clk_lock);
+
+ if (clk) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+ }
+}
+
+
+
+/**
+ * sunxi_mux_clk_setup() - Setup function for muxes
+ */
+
+#define SUNXI_MUX_GATE_WIDTH 2
+
+struct mux_data {
+ u8 shift;
+};
+
+static const __initconst struct mux_data cpu_data = {
+ .shift = 16,
+};
+
+static const __initconst struct mux_data apb1_mux_data = {
+ .shift = 24,
+};
+
+static void __init sunxi_mux_clk_setup(struct device_node *node,
+ struct mux_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parents[5];
+ void *reg;
+ int i = 0;
+
+ reg = of_iomap(node, 0);
+
+ while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
+ data->shift, SUNXI_MUX_GATE_WIDTH,
+ 0, &clk_lock);
+
+ if (clk) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+ }
+}
+
+
+
+/**
+ * sunxi_divider_clk_setup() - Setup function for simple divider clocks
+ */
+
+#define SUNXI_DIVISOR_WIDTH 2
+
+struct div_data {
+ u8 shift;
+ u8 pow;
+};
+
+static const __initconst struct div_data axi_data = {
+ .shift = 0,
+ .pow = 0,
+};
+
+static const __initconst struct div_data ahb_data = {
+ .shift = 4,
+ .pow = 1,
+};
+
+static const __initconst struct div_data apb0_data = {
+ .shift = 8,
+ .pow = 1,
+};
+
+static void __init sunxi_divider_clk_setup(struct device_node *node,
+ struct div_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *clk_parent;
+ void *reg;
+
+ reg = of_iomap(node, 0);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
+ reg, data->shift, SUNXI_DIVISOR_WIDTH,
+ data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
+ &clk_lock);
+ if (clk) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+ }
+}
+
+
+
+/**
+ * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
+ */
+
+#define SUNXI_GATES_MAX_SIZE 64
+
+struct gates_data {
+ DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+};
+
+static const __initconst struct gates_data axi_gates_data = {
+ .mask = {1},
+};
+
+static const __initconst struct gates_data ahb_gates_data = {
+ .mask = {0x7F77FFF, 0x14FB3F},
+};
+
+static const __initconst struct gates_data apb0_gates_data = {
+ .mask = {0x4EF},
+};
+
+static const __initconst struct gates_data apb1_gates_data = {
+ .mask = {0xFF00F7},
+};
+
+static void __init sunxi_gates_clk_setup(struct device_node *node,
+ struct gates_data *data)
+{
+ struct clk_onecell_data *clk_data;
+ const char *clk_parent;
+ const char *clk_name;
+ void *reg;
+ int qty;
+ int i = 0;
+ int j = 0;
+ int ignore;
+
+ reg = of_iomap(node, 0);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ /* Worst-case size approximation and memory allocation */
+ qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE);
+ clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+ clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) {
+ of_property_read_string_index(node, "clock-output-names",
+ j, &clk_name);
+
+ /* No driver claims this clock, but it should remain gated */
+ ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0;
+
+ clk_data->clks[i] = clk_register_gate(NULL, clk_name,
+ clk_parent, ignore,
+ reg + 4 * (i/32), i % 32,
+ 0, &clk_lock);
+ WARN_ON(IS_ERR(clk_data->clks[i]));
+
+ j++;
+ }
+
+ /* Adjust to the real max */
+ clk_data->clk_num = i;
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+/* Matches for of_clk_init */
+static const __initconst struct of_device_id clk_match[] = {
+ {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,},
+ {}
+};
+
+/* Matches for factors clocks */
+static const __initconst struct of_device_id clk_factors_match[] = {
+ {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,},
+ {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,},
+ {}
+};
+
+/* Matches for divider clocks */
+static const __initconst struct of_device_id clk_div_match[] = {
+ {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,},
+ {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,},
+ {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,},
+ {}
+};
+
+/* Matches for mux clocks */
+static const __initconst struct of_device_id clk_mux_match[] = {
+ {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,},
+ {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,},
+ {}
+};
+
+/* Matches for gate clocks */
+static const __initconst struct of_device_id clk_gates_match[] = {
+ {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,},
+ {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,},
+ {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,},
+ {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,},
+ {}
+};
+
+static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
+ void *function)
+{
+ struct device_node *np;
+ const struct div_data *data;
+ const struct of_device_id *match;
+ void (*setup_function)(struct device_node *, const void *) = function;
+
+ for_each_matching_node(np, clk_match) {
+ match = of_match_node(clk_match, np);
+ data = match->data;
+ setup_function(np, data);
+ }
+}
+
+void __init sunxi_init_clocks(void)
+{
+ /* Register all the simple sunxi clocks on DT */
+ of_clk_init(clk_match);
+
+ /* Register factor clocks */
+ of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
+
+ /* Register divider clocks */
+ of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
+
+ /* Register mux clocks */
+ of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
+
+ /* Register gate clocks */
+ of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
+}
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
new file mode 100644
index 000000000..f49fac2d1
--- /dev/null
+++ b/drivers/clk/tegra/Makefile
@@ -0,0 +1,12 @@
+obj-y += clk.o
+obj-y += clk-audio-sync.o
+obj-y += clk-divider.o
+obj-y += clk-periph.o
+obj-y += clk-periph-gate.o
+obj-y += clk-pll.o
+obj-y += clk-pll-out.o
+obj-y += clk-super.o
+
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
diff --git a/drivers/clk/tegra/clk-audio-sync.c b/drivers/clk/tegra/clk-audio-sync.c
new file mode 100644
index 000000000..c0f7843e8
--- /dev/null
+++ b/drivers/clk/tegra/clk-audio-sync.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include "clk.h"
+
+static unsigned long clk_sync_source_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
+
+ return sync->rate;
+}
+
+static long clk_sync_source_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
+
+ if (rate > sync->max_rate)
+ return -EINVAL;
+ else
+ return rate;
+}
+
+static int clk_sync_source_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
+
+ sync->rate = rate;
+ return 0;
+}
+
+const struct clk_ops tegra_clk_sync_source_ops = {
+ .round_rate = clk_sync_source_round_rate,
+ .set_rate = clk_sync_source_set_rate,
+ .recalc_rate = clk_sync_source_recalc_rate,
+};
+
+struct clk *tegra_clk_register_sync_source(const char *name,
+ unsigned long rate, unsigned long max_rate)
+{
+ struct tegra_clk_sync_source *sync;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ sync = kzalloc(sizeof(*sync), GFP_KERNEL);
+ if (!sync) {
+ pr_err("%s: could not allocate sync source clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sync->rate = rate;
+ sync->max_rate = max_rate;
+
+ init.ops = &tegra_clk_sync_source_ops;
+ init.name = name;
+ init.flags = CLK_IS_ROOT;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ sync->hw.init = &init;
+
+ clk = clk_register(NULL, &sync->hw);
+ if (IS_ERR(clk))
+ kfree(sync);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c
new file mode 100644
index 000000000..4d75b1f37
--- /dev/null
+++ b/drivers/clk/tegra/clk-divider.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+
+#include "clk.h"
+
+#define pll_out_override(p) (BIT((p->shift - 6)))
+#define div_mask(d) ((1 << (d->width)) - 1)
+#define get_mul(d) (1 << d->frac_width)
+#define get_max_div(d) div_mask(d)
+
+#define PERIPH_CLK_UART_DIV_ENB BIT(24)
+
+static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
+ unsigned long parent_rate)
+{
+ s64 divider_ux1 = parent_rate;
+ u8 flags = divider->flags;
+ int mul;
+
+ if (!rate)
+ return 0;
+
+ mul = get_mul(divider);
+
+ if (!(flags & TEGRA_DIVIDER_INT))
+ divider_ux1 *= mul;
+
+ if (flags & TEGRA_DIVIDER_ROUND_UP)
+ divider_ux1 += rate - 1;
+
+ do_div(divider_ux1, rate);
+
+ if (flags & TEGRA_DIVIDER_INT)
+ divider_ux1 *= mul;
+
+ divider_ux1 -= mul;
+
+ if (divider_ux1 < 0)
+ return 0;
+
+ if (divider_ux1 > get_max_div(divider))
+ return -EINVAL;
+
+ return divider_ux1;
+}
+
+static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
+ u32 reg;
+ int div, mul;
+ u64 rate = parent_rate;
+
+ reg = readl_relaxed(divider->reg) >> divider->shift;
+ div = reg & div_mask(divider);
+
+ mul = get_mul(divider);
+ div += mul;
+
+ rate *= mul;
+ rate += div - 1;
+ do_div(rate, div);
+
+ return rate;
+}
+
+static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
+ int div, mul;
+ unsigned long output_rate = *prate;
+
+ if (!rate)
+ return output_rate;
+
+ div = get_div(divider, rate, output_rate);
+ if (div < 0)
+ return *prate;
+
+ mul = get_mul(divider);
+
+ return DIV_ROUND_UP(output_rate * mul, div + mul);
+}
+
+static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
+ int div;
+ unsigned long flags = 0;
+ u32 val;
+
+ div = get_div(divider, rate, parent_rate);
+ if (div < 0)
+ return div;
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+
+ val = readl_relaxed(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ val |= div << divider->shift;
+
+ if (divider->flags & TEGRA_DIVIDER_UART) {
+ if (div)
+ val |= PERIPH_CLK_UART_DIV_ENB;
+ else
+ val &= ~PERIPH_CLK_UART_DIV_ENB;
+ }
+
+ if (divider->flags & TEGRA_DIVIDER_FIXED)
+ val |= pll_out_override(divider);
+
+ writel_relaxed(val, divider->reg);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return 0;
+}
+
+const struct clk_ops tegra_clk_frac_div_ops = {
+ .recalc_rate = clk_frac_div_recalc_rate,
+ .set_rate = clk_frac_div_set_rate,
+ .round_rate = clk_frac_div_round_rate,
+};
+
+struct clk *tegra_clk_register_divider(const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
+ u8 frac_width, spinlock_t *lock)
+{
+ struct tegra_clk_frac_div *divider;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ divider = kzalloc(sizeof(*divider), GFP_KERNEL);
+ if (!divider) {
+ pr_err("%s: could not allocate fractional divider clk\n",
+ __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &tegra_clk_frac_div_ops;
+ init.flags = flags;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+
+ divider->reg = reg;
+ divider->shift = shift;
+ divider->width = width;
+ divider->frac_width = frac_width;
+ divider->lock = lock;
+ divider->flags = clk_divider_flags;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ divider->hw.init = &init;
+
+ clk = clk_register(NULL, &divider->hw);
+ if (IS_ERR(clk))
+ kfree(divider);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c
new file mode 100644
index 000000000..bafee9895
--- /dev/null
+++ b/drivers/clk/tegra/clk-periph-gate.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/tegra-soc.h>
+
+#include "clk.h"
+
+static DEFINE_SPINLOCK(periph_ref_lock);
+
+/* Macros to assist peripheral gate clock */
+#define read_enb(gate) \
+ readl_relaxed(gate->clk_base + (gate->regs->enb_reg))
+#define write_enb_set(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->enb_set_reg))
+#define write_enb_clr(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->enb_clr_reg))
+
+#define read_rst(gate) \
+ readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
+#define write_rst_set(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
+#define write_rst_clr(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
+
+#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32))
+
+#define LVL2_CLK_GATE_OVRE 0x554
+
+/* Peripheral gate clock ops */
+static int clk_periph_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ int state = 1;
+
+ if (!(read_enb(gate) & periph_clk_to_bit(gate)))
+ state = 0;
+
+ if (!(gate->flags & TEGRA_PERIPH_NO_RESET))
+ if (read_rst(gate) & periph_clk_to_bit(gate))
+ state = 0;
+
+ return state;
+}
+
+static int clk_periph_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&periph_ref_lock, flags);
+
+ gate->enable_refcnt[gate->clk_num]++;
+ if (gate->enable_refcnt[gate->clk_num] > 1) {
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+ return 0;
+ }
+
+ write_enb_set(periph_clk_to_bit(gate), gate);
+ udelay(2);
+
+ if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
+ !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
+ if (read_rst(gate) & periph_clk_to_bit(gate)) {
+ udelay(5); /* reset propogation delay */
+ write_rst_clr(periph_clk_to_bit(gate), gate);
+ }
+ }
+
+ if (gate->flags & TEGRA_PERIPH_WAR_1005168) {
+ writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);
+ udelay(1);
+ writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
+ }
+
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+
+ return 0;
+}
+
+static void clk_periph_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&periph_ref_lock, flags);
+
+ gate->enable_refcnt[gate->clk_num]--;
+ if (gate->enable_refcnt[gate->clk_num] > 0) {
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+ return;
+ }
+
+ /*
+ * If peripheral is in the APB bus then read the APB bus to
+ * flush the write operation in apb bus. This will avoid the
+ * peripheral access after disabling clock
+ */
+ if (gate->flags & TEGRA_PERIPH_ON_APB)
+ tegra_read_chipid();
+
+ write_enb_clr(periph_clk_to_bit(gate), gate);
+
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+}
+
+void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert)
+{
+ if (gate->flags & TEGRA_PERIPH_NO_RESET)
+ return;
+
+ if (assert) {
+ /*
+ * If peripheral is in the APB bus then read the APB bus to
+ * flush the write operation in apb bus. This will avoid the
+ * peripheral access after disabling clock
+ */
+ if (gate->flags & TEGRA_PERIPH_ON_APB)
+ tegra_read_chipid();
+
+ write_rst_set(periph_clk_to_bit(gate), gate);
+ } else {
+ write_rst_clr(periph_clk_to_bit(gate), gate);
+ }
+}
+
+const struct clk_ops tegra_clk_periph_gate_ops = {
+ .is_enabled = clk_periph_is_enabled,
+ .enable = clk_periph_enable,
+ .disable = clk_periph_disable,
+};
+
+struct clk *tegra_clk_register_periph_gate(const char *name,
+ const char *parent_name, u8 gate_flags, void __iomem *clk_base,
+ unsigned long flags, int clk_num,
+ struct tegra_clk_periph_regs *pregs, int *enable_refcnt)
+{
+ struct tegra_clk_periph_gate *gate;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate) {
+ pr_err("%s: could not allocate periph gate clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.flags = flags;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.ops = &tegra_clk_periph_gate_ops;
+
+ gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC;
+ gate->clk_base = clk_base;
+ gate->clk_num = clk_num;
+ gate->flags = gate_flags;
+ gate->enable_refcnt = enable_refcnt;
+ gate->regs = pregs;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ gate->hw.init = &init;
+
+ clk = clk_register(NULL, &gate->hw);
+ if (IS_ERR(clk))
+ kfree(gate);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
new file mode 100644
index 000000000..b2309d37a
--- /dev/null
+++ b/drivers/clk/tegra/clk-periph.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include "clk.h"
+
+static u8 clk_periph_get_parent(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *mux_ops = periph->mux_ops;
+ struct clk_hw *mux_hw = &periph->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ return mux_ops->get_parent(mux_hw);
+}
+
+static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *mux_ops = periph->mux_ops;
+ struct clk_hw *mux_hw = &periph->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ return mux_ops->set_parent(mux_hw, index);
+}
+
+static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+
+ div_hw->clk = hw->clk;
+
+ return div_ops->recalc_rate(div_hw, parent_rate);
+}
+
+static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+
+ div_hw->clk = hw->clk;
+
+ return div_ops->round_rate(div_hw, rate, prate);
+}
+
+static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+
+ div_hw->clk = hw->clk;
+
+ return div_ops->set_rate(div_hw, rate, parent_rate);
+}
+
+static int clk_periph_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ gate_hw->clk = hw->clk;
+
+ return gate_ops->is_enabled(gate_hw);
+}
+
+static int clk_periph_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ gate_hw->clk = hw->clk;
+
+ return gate_ops->enable(gate_hw);
+}
+
+static void clk_periph_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ gate_ops->disable(gate_hw);
+}
+
+void tegra_periph_reset_deassert(struct clk *c)
+{
+ struct clk_hw *hw = __clk_get_hw(c);
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ struct tegra_clk_periph_gate *gate;
+
+ if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
+ gate = to_clk_periph_gate(hw);
+ if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
+ WARN_ON(1);
+ return;
+ }
+ } else {
+ gate = &periph->gate;
+ }
+
+ tegra_periph_reset(gate, 0);
+}
+EXPORT_SYMBOL(tegra_periph_reset_deassert);
+
+void tegra_periph_reset_assert(struct clk *c)
+{
+ struct clk_hw *hw = __clk_get_hw(c);
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ struct tegra_clk_periph_gate *gate;
+
+ if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
+ gate = to_clk_periph_gate(hw);
+ if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
+ WARN_ON(1);
+ return;
+ }
+ } else {
+ gate = &periph->gate;
+ }
+
+ tegra_periph_reset(gate, 1);
+}
+EXPORT_SYMBOL(tegra_periph_reset_assert);
+
+const struct clk_ops tegra_clk_periph_ops = {
+ .get_parent = clk_periph_get_parent,
+ .set_parent = clk_periph_set_parent,
+ .recalc_rate = clk_periph_recalc_rate,
+ .round_rate = clk_periph_round_rate,
+ .set_rate = clk_periph_set_rate,
+ .is_enabled = clk_periph_is_enabled,
+ .enable = clk_periph_enable,
+ .disable = clk_periph_disable,
+};
+
+const struct clk_ops tegra_clk_periph_nodiv_ops = {
+ .get_parent = clk_periph_get_parent,
+ .set_parent = clk_periph_set_parent,
+ .is_enabled = clk_periph_is_enabled,
+ .enable = clk_periph_enable,
+ .disable = clk_periph_disable,
+};
+
+static struct clk *_tegra_clk_register_periph(const char *name,
+ const char **parent_names, int num_parents,
+ struct tegra_clk_periph *periph,
+ void __iomem *clk_base, u32 offset, bool div,
+ unsigned long flags)
+{
+ struct clk *clk;
+ struct clk_init_data init;
+
+ init.name = name;
+ init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ periph->hw.init = &init;
+ periph->magic = TEGRA_CLK_PERIPH_MAGIC;
+ periph->mux.reg = clk_base + offset;
+ periph->divider.reg = div ? (clk_base + offset) : NULL;
+ periph->gate.clk_base = clk_base;
+
+ clk = clk_register(NULL, &periph->hw);
+ if (IS_ERR(clk))
+ return clk;
+
+ periph->mux.hw.clk = clk;
+ periph->divider.hw.clk = div ? clk : NULL;
+ periph->gate.hw.clk = clk;
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_periph(const char *name,
+ const char **parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset, unsigned long flags)
+{
+ return _tegra_clk_register_periph(name, parent_names, num_parents,
+ periph, clk_base, offset, true, flags);
+}
+
+struct clk *tegra_clk_register_periph_nodiv(const char *name,
+ const char **parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset)
+{
+ return _tegra_clk_register_periph(name, parent_names, num_parents,
+ periph, clk_base, offset, false, CLK_SET_RATE_PARENT);
+}
diff --git a/drivers/clk/tegra/clk-pll-out.c b/drivers/clk/tegra/clk-pll-out.c
new file mode 100644
index 000000000..3598987a4
--- /dev/null
+++ b/drivers/clk/tegra/clk-pll-out.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+
+#include "clk.h"
+
+#define pll_out_enb(p) (BIT(p->enb_bit_idx))
+#define pll_out_rst(p) (BIT(p->rst_bit_idx))
+
+static int clk_pll_out_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+ u32 val = readl_relaxed(pll_out->reg);
+ int state;
+
+ state = (val & pll_out_enb(pll_out)) ? 1 : 0;
+ if (!(val & (pll_out_rst(pll_out))))
+ state = 0;
+ return state;
+}
+
+static int clk_pll_out_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll_out->lock)
+ spin_lock_irqsave(pll_out->lock, flags);
+
+ val = readl_relaxed(pll_out->reg);
+
+ val |= (pll_out_enb(pll_out) | pll_out_rst(pll_out));
+
+ writel_relaxed(val, pll_out->reg);
+ udelay(2);
+
+ if (pll_out->lock)
+ spin_unlock_irqrestore(pll_out->lock, flags);
+
+ return 0;
+}
+
+static void clk_pll_out_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll_out->lock)
+ spin_lock_irqsave(pll_out->lock, flags);
+
+ val = readl_relaxed(pll_out->reg);
+
+ val &= ~(pll_out_enb(pll_out) | pll_out_rst(pll_out));
+
+ writel_relaxed(val, pll_out->reg);
+ udelay(2);
+
+ if (pll_out->lock)
+ spin_unlock_irqrestore(pll_out->lock, flags);
+}
+
+const struct clk_ops tegra_clk_pll_out_ops = {
+ .is_enabled = clk_pll_out_is_enabled,
+ .enable = clk_pll_out_enable,
+ .disable = clk_pll_out_disable,
+};
+
+struct clk *tegra_clk_register_pll_out(const char *name,
+ const char *parent_name, void __iomem *reg, u8 enb_bit_idx,
+ u8 rst_bit_idx, unsigned long flags, u8 pll_out_flags,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll_out *pll_out;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll_out = kzalloc(sizeof(*pll_out), GFP_KERNEL);
+ if (!pll_out)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &tegra_clk_pll_out_ops;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ init.flags = flags;
+
+ pll_out->reg = reg;
+ pll_out->enb_bit_idx = enb_bit_idx;
+ pll_out->rst_bit_idx = rst_bit_idx;
+ pll_out->flags = pll_out_flags;
+ pll_out->lock = lock;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ pll_out->hw.init = &init;
+
+ clk = clk_register(NULL, &pll_out->hw);
+ if (IS_ERR(clk))
+ kfree(pll_out);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
new file mode 100644
index 000000000..17c2cc086
--- /dev/null
+++ b/drivers/clk/tegra/clk-pll.c
@@ -0,0 +1,1582 @@
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+
+#include "clk.h"
+
+#define PLL_BASE_BYPASS BIT(31)
+#define PLL_BASE_ENABLE BIT(30)
+#define PLL_BASE_REF_ENABLE BIT(29)
+#define PLL_BASE_OVERRIDE BIT(28)
+
+#define PLL_BASE_DIVP_SHIFT 20
+#define PLL_BASE_DIVP_WIDTH 3
+#define PLL_BASE_DIVN_SHIFT 8
+#define PLL_BASE_DIVN_WIDTH 10
+#define PLL_BASE_DIVM_SHIFT 0
+#define PLL_BASE_DIVM_WIDTH 5
+#define PLLU_POST_DIVP_MASK 0x1
+
+#define PLL_MISC_DCCON_SHIFT 20
+#define PLL_MISC_CPCON_SHIFT 8
+#define PLL_MISC_CPCON_WIDTH 4
+#define PLL_MISC_CPCON_MASK ((1 << PLL_MISC_CPCON_WIDTH) - 1)
+#define PLL_MISC_LFCON_SHIFT 4
+#define PLL_MISC_LFCON_WIDTH 4
+#define PLL_MISC_LFCON_MASK ((1 << PLL_MISC_LFCON_WIDTH) - 1)
+#define PLL_MISC_VCOCON_SHIFT 0
+#define PLL_MISC_VCOCON_WIDTH 4
+#define PLL_MISC_VCOCON_MASK ((1 << PLL_MISC_VCOCON_WIDTH) - 1)
+
+#define OUT_OF_TABLE_CPCON 8
+
+#define PMC_PLLP_WB0_OVERRIDE 0xf8
+#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE BIT(12)
+#define PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE BIT(11)
+
+#define PLL_POST_LOCK_DELAY 50
+
+#define PLLDU_LFCON_SET_DIVN 600
+
+#define PLLE_BASE_DIVCML_SHIFT 24
+#define PLLE_BASE_DIVCML_WIDTH 4
+#define PLLE_BASE_DIVP_SHIFT 16
+#define PLLE_BASE_DIVP_WIDTH 7
+#define PLLE_BASE_DIVN_SHIFT 8
+#define PLLE_BASE_DIVN_WIDTH 8
+#define PLLE_BASE_DIVM_SHIFT 0
+#define PLLE_BASE_DIVM_WIDTH 8
+
+#define PLLE_MISC_SETUP_BASE_SHIFT 16
+#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT)
+#define PLLE_MISC_LOCK_ENABLE BIT(9)
+#define PLLE_MISC_READY BIT(15)
+#define PLLE_MISC_SETUP_EX_SHIFT 2
+#define PLLE_MISC_SETUP_EX_MASK (3 << PLLE_MISC_SETUP_EX_SHIFT)
+#define PLLE_MISC_SETUP_MASK (PLLE_MISC_SETUP_BASE_MASK | \
+ PLLE_MISC_SETUP_EX_MASK)
+#define PLLE_MISC_SETUP_VALUE (7 << PLLE_MISC_SETUP_BASE_SHIFT)
+
+#define PLLE_SS_CTRL 0x68
+#define PLLE_SS_DISABLE (7 << 10)
+
+#define PLLE_AUX_PLLP_SEL BIT(2)
+#define PLLE_AUX_ENABLE_SWCTL BIT(4)
+#define PLLE_AUX_SEQ_ENABLE BIT(24)
+#define PLLE_AUX_PLLRE_SEL BIT(28)
+
+#define PLLE_MISC_PLLE_PTS BIT(8)
+#define PLLE_MISC_IDDQ_SW_VALUE BIT(13)
+#define PLLE_MISC_IDDQ_SW_CTRL BIT(14)
+#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4
+#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT)
+#define PLLE_MISC_VREG_CTRL_SHIFT 2
+#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT)
+
+#define PLLCX_MISC_STROBE BIT(31)
+#define PLLCX_MISC_RESET BIT(30)
+#define PLLCX_MISC_SDM_DIV_SHIFT 28
+#define PLLCX_MISC_SDM_DIV_MASK (0x3 << PLLCX_MISC_SDM_DIV_SHIFT)
+#define PLLCX_MISC_FILT_DIV_SHIFT 26
+#define PLLCX_MISC_FILT_DIV_MASK (0x3 << PLLCX_MISC_FILT_DIV_SHIFT)
+#define PLLCX_MISC_ALPHA_SHIFT 18
+#define PLLCX_MISC_DIV_LOW_RANGE \
+ ((0x1 << PLLCX_MISC_SDM_DIV_SHIFT) | \
+ (0x1 << PLLCX_MISC_FILT_DIV_SHIFT))
+#define PLLCX_MISC_DIV_HIGH_RANGE \
+ ((0x2 << PLLCX_MISC_SDM_DIV_SHIFT) | \
+ (0x2 << PLLCX_MISC_FILT_DIV_SHIFT))
+#define PLLCX_MISC_COEF_LOW_RANGE \
+ ((0x14 << PLLCX_MISC_KA_SHIFT) | (0x38 << PLLCX_MISC_KB_SHIFT))
+#define PLLCX_MISC_KA_SHIFT 2
+#define PLLCX_MISC_KB_SHIFT 9
+#define PLLCX_MISC_DEFAULT (PLLCX_MISC_COEF_LOW_RANGE | \
+ (0x19 << PLLCX_MISC_ALPHA_SHIFT) | \
+ PLLCX_MISC_DIV_LOW_RANGE | \
+ PLLCX_MISC_RESET)
+#define PLLCX_MISC1_DEFAULT 0x000d2308
+#define PLLCX_MISC2_DEFAULT 0x30211200
+#define PLLCX_MISC3_DEFAULT 0x200
+
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
+#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0
+#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27)
+
+#define PMC_SATA_PWRGT 0x1ac
+#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)
+#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4)
+
+#define pll_readl(offset, p) readl_relaxed(p->clk_base + offset)
+#define pll_readl_base(p) pll_readl(p->params->base_reg, p)
+#define pll_readl_misc(p) pll_readl(p->params->misc_reg, p)
+
+#define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset)
+#define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p)
+#define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p)
+
+#define mask(w) ((1 << (w)) - 1)
+#define divm_mask(p) mask(p->divm_width)
+#define divn_mask(p) mask(p->divn_width)
+#define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \
+ mask(p->divp_width))
+
+#define divm_max(p) (divm_mask(p))
+#define divn_max(p) (divn_mask(p))
+#define divp_max(p) (1 << (divp_mask(p)))
+
+
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */
+#define PLLXC_PDIV_MAX 14
+
+/* non-monotonic mapping below is not a typo */
+static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = {
+ /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */
+ /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32
+};
+
+#define PLLCX_PDIV_MAX 7
+static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = {
+ /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7 */
+ /* p: */ 1, 2, 3, 4, 6, 8, 12, 16
+};
+#endif
+
+static void clk_pll_enable_lock(struct tegra_clk_pll *pll)
+{
+ u32 val;
+
+ if (!(pll->flags & TEGRA_PLL_USE_LOCK))
+ return;
+
+ if (!(pll->flags & TEGRA_PLL_HAS_LOCK_ENABLE))
+ return;
+
+ val = pll_readl_misc(pll);
+ val |= BIT(pll->params->lock_enable_bit_idx);
+ pll_writel_misc(val, pll);
+}
+
+static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll)
+{
+ int i;
+ u32 val, lock_mask;
+ void __iomem *lock_addr;
+
+ if (!(pll->flags & TEGRA_PLL_USE_LOCK)) {
+ udelay(pll->params->lock_delay);
+ return 0;
+ }
+
+ lock_addr = pll->clk_base;
+ if (pll->flags & TEGRA_PLL_LOCK_MISC)
+ lock_addr += pll->params->misc_reg;
+ else
+ lock_addr += pll->params->base_reg;
+
+ lock_mask = pll->params->lock_mask;
+
+ for (i = 0; i < pll->params->lock_delay; i++) {
+ val = readl_relaxed(lock_addr);
+ if ((val & lock_mask) == lock_mask) {
+ udelay(PLL_POST_LOCK_DELAY);
+ return 0;
+ }
+ udelay(2); /* timeout = 2 * lock time */
+ }
+
+ pr_err("%s: Timed out waiting for pll %s lock\n", __func__,
+ __clk_get_name(pll->hw.clk));
+
+ return -1;
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ if (pll->flags & TEGRA_PLLM) {
+ val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)
+ return val & PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE ? 1 : 0;
+ }
+
+ val = pll_readl_base(pll);
+
+ return val & PLL_BASE_ENABLE ? 1 : 0;
+}
+
+static void _clk_pll_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ clk_pll_enable_lock(pll);
+
+ val = pll_readl_base(pll);
+ if (pll->flags & TEGRA_PLL_BYPASS)
+ val &= ~PLL_BASE_BYPASS;
+ val |= PLL_BASE_ENABLE;
+ pll_writel_base(val, pll);
+
+ if (pll->flags & TEGRA_PLLM) {
+ val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
+ writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ }
+}
+
+static void _clk_pll_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ val = pll_readl_base(pll);
+ if (pll->flags & TEGRA_PLL_BYPASS)
+ val &= ~PLL_BASE_BYPASS;
+ val &= ~PLL_BASE_ENABLE;
+ pll_writel_base(val, pll);
+
+ if (pll->flags & TEGRA_PLLM) {
+ val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
+ writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ }
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int ret;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_enable(hw);
+
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_disable(hw);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int _get_table_rate(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table *sel;
+
+ for (sel = pll->freq_table; sel->input_rate != 0; sel++)
+ if (sel->input_rate == parent_rate &&
+ sel->output_rate == rate)
+ break;
+
+ if (sel->input_rate == 0)
+ return -EINVAL;
+
+ cfg->input_rate = sel->input_rate;
+ cfg->output_rate = sel->output_rate;
+ cfg->m = sel->m;
+ cfg->n = sel->n;
+ cfg->p = sel->p;
+ cfg->cpcon = sel->cpcon;
+
+ return 0;
+}
+
+static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct pdiv_map *p_tohw = pll->params->pdiv_tohw;
+ unsigned long cfreq;
+ u32 p_div = 0;
+
+ switch (parent_rate) {
+ case 12000000:
+ case 26000000:
+ cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000;
+ break;
+ case 13000000:
+ cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000;
+ break;
+ case 16800000:
+ case 19200000:
+ cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
+ break;
+ case 9600000:
+ case 28800000:
+ /*
+ * PLL_P_OUT1 rate is not listed in PLLA table
+ */
+ cfreq = parent_rate/(parent_rate/1000000);
+ break;
+ default:
+ pr_err("%s Unexpected reference rate %lu\n",
+ __func__, parent_rate);
+ BUG();
+ }
+
+ /* Raise VCO to guarantee 0.5% accuracy */
+ for (cfg->output_rate = rate; cfg->output_rate < 200 * cfreq;
+ cfg->output_rate <<= 1)
+ p_div++;
+
+ cfg->m = parent_rate / cfreq;
+ cfg->n = cfg->output_rate / cfreq;
+ cfg->cpcon = OUT_OF_TABLE_CPCON;
+
+ if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) ||
+ (1 << p_div) > divp_max(pll)
+ || cfg->output_rate > pll->params->vco_max) {
+ pr_err("%s: Failed to set %s rate %lu\n",
+ __func__, __clk_get_name(hw->clk), rate);
+ return -EINVAL;
+ }
+
+ if (p_tohw) {
+ p_div = 1 << p_div;
+ while (p_tohw->pdiv) {
+ if (p_div <= p_tohw->pdiv) {
+ cfg->p = p_tohw->hw_val;
+ break;
+ }
+ p_tohw++;
+ }
+ if (!p_tohw->pdiv)
+ return -EINVAL;
+ } else
+ cfg->p = p_div;
+
+ return 0;
+}
+
+static void _update_pll_mnp(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg)
+{
+ u32 val;
+
+ val = pll_readl_base(pll);
+
+ val &= ~((divm_mask(pll) << pll->divm_shift) |
+ (divn_mask(pll) << pll->divn_shift) |
+ (divp_mask(pll) << pll->divp_shift));
+ val |= ((cfg->m << pll->divm_shift) |
+ (cfg->n << pll->divn_shift) |
+ (cfg->p << pll->divp_shift));
+
+ pll_writel_base(val, pll);
+}
+
+static void _get_pll_mnp(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg)
+{
+ u32 val;
+
+ val = pll_readl_base(pll);
+
+ cfg->m = (val >> pll->divm_shift) & (divm_mask(pll));
+ cfg->n = (val >> pll->divn_shift) & (divn_mask(pll));
+ cfg->p = (val >> pll->divp_shift) & (divp_mask(pll));
+}
+
+static void _update_pll_cpcon(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate)
+{
+ u32 val;
+
+ val = pll_readl_misc(pll);
+
+ val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT);
+ val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT;
+
+ if (pll->flags & TEGRA_PLL_SET_LFCON) {
+ val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT);
+ if (cfg->n >= PLLDU_LFCON_SET_DIVN)
+ val |= 1 << PLL_MISC_LFCON_SHIFT;
+ } else if (pll->flags & TEGRA_PLL_SET_DCCON) {
+ val &= ~(1 << PLL_MISC_DCCON_SHIFT);
+ if (rate >= (pll->params->vco_max >> 1))
+ val |= 1 << PLL_MISC_DCCON_SHIFT;
+ }
+
+ pll_writel_misc(val, pll);
+}
+
+static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ int state, ret = 0;
+
+ state = clk_pll_is_enabled(hw);
+
+ if (state)
+ _clk_pll_disable(hw);
+
+ _update_pll_mnp(pll, cfg);
+
+ if (pll->flags & TEGRA_PLL_HAS_CPCON)
+ _update_pll_cpcon(pll, cfg, rate);
+
+ if (state) {
+ _clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll);
+ }
+
+ return ret;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ unsigned long flags = 0;
+ int ret = 0;
+
+ if (pll->flags & TEGRA_PLL_FIXED) {
+ if (rate != pll->fixed_rate) {
+ pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
+ __func__, __clk_get_name(hw->clk),
+ pll->fixed_rate, rate);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (_get_table_rate(hw, &cfg, rate, parent_rate) &&
+ _calc_rate(hw, &cfg, rate, parent_rate))
+ return -EINVAL;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _get_pll_mnp(pll, &old_cfg);
+
+ if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p)
+ ret = _program_pll(hw, &cfg, rate);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg;
+ u64 output_rate = *prate;
+
+ if (pll->flags & TEGRA_PLL_FIXED)
+ return pll->fixed_rate;
+
+ /* PLLM is used for memory; we do not change rate */
+ if (pll->flags & TEGRA_PLLM)
+ return __clk_get_rate(hw->clk);
+
+ if (_get_table_rate(hw, &cfg, rate, *prate) &&
+ _calc_rate(hw, &cfg, rate, *prate))
+ return -EINVAL;
+
+ output_rate *= cfg.n;
+ do_div(output_rate, cfg.m * (1 << cfg.p));
+
+ return output_rate;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg;
+ struct pdiv_map *p_tohw = pll->params->pdiv_tohw;
+ u32 val;
+ u64 rate = parent_rate;
+ int pdiv;
+
+ val = pll_readl_base(pll);
+
+ if ((pll->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS))
+ return parent_rate;
+
+ if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) {
+ struct tegra_clk_pll_freq_table sel;
+ if (_get_table_rate(hw, &sel, pll->fixed_rate, parent_rate)) {
+ pr_err("Clock %s has unknown fixed frequency\n",
+ __clk_get_name(hw->clk));
+ BUG();
+ }
+ return pll->fixed_rate;
+ }
+
+ _get_pll_mnp(pll, &cfg);
+
+ if (p_tohw) {
+ while (p_tohw->pdiv) {
+ if (cfg.p == p_tohw->hw_val) {
+ pdiv = p_tohw->pdiv;
+ break;
+ }
+ p_tohw++;
+ }
+
+ if (!p_tohw->pdiv) {
+ WARN_ON(1);
+ pdiv = 1;
+ }
+ } else
+ pdiv = 1 << cfg.p;
+
+ cfg.m *= pdiv;
+
+ rate *= cfg.n;
+ do_div(rate, cfg.m);
+
+ return rate;
+}
+
+static int clk_plle_training(struct tegra_clk_pll *pll)
+{
+ u32 val;
+ unsigned long timeout;
+
+ if (!pll->pmc)
+ return -ENOSYS;
+
+ /*
+ * PLLE is already disabled, and setup cleared;
+ * create falling edge on PLLE IDDQ input.
+ */
+ val = readl(pll->pmc + PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ writel(val, pll->pmc + PMC_SATA_PWRGT);
+
+ val = readl(pll->pmc + PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
+ writel(val, pll->pmc + PMC_SATA_PWRGT);
+
+ val = readl(pll->pmc + PMC_SATA_PWRGT);
+ val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ writel(val, pll->pmc + PMC_SATA_PWRGT);
+
+ val = pll_readl_misc(pll);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (1) {
+ val = pll_readl_misc(pll);
+ if (val & PLLE_MISC_READY)
+ break;
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: timeout waiting for PLLE\n", __func__);
+ return -EBUSY;
+ }
+ udelay(300);
+ }
+
+ return 0;
+}
+
+static int clk_plle_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk));
+ struct tegra_clk_pll_freq_table sel;
+ u32 val;
+ int err;
+
+ if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
+ return -EINVAL;
+
+ clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
+ pll_writel_misc(val, pll);
+
+ val = pll_readl_misc(pll);
+ if (!(val & PLLE_MISC_READY)) {
+ err = clk_plle_training(pll);
+ if (err)
+ return err;
+ }
+
+ if (pll->flags & TEGRA_PLLE_CONFIGURE) {
+ /* configure dividers */
+ val = pll_readl_base(pll);
+ val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll));
+ val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << pll->divm_shift;
+ val |= sel.n << pll->divn_shift;
+ val |= sel.p << pll->divp_shift;
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+ }
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_SETUP_VALUE;
+ val |= PLLE_MISC_LOCK_ENABLE;
+ pll_writel_misc(val, pll);
+
+ val = readl(pll->clk_base + PLLE_SS_CTRL);
+ val |= PLLE_SS_DISABLE;
+ writel(val, pll->clk_base + PLLE_SS_CTRL);
+
+ val |= pll_readl_base(pll);
+ val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+ pll_writel_base(val, pll);
+
+ clk_pll_wait_for_lock(pll);
+
+ return 0;
+}
+
+static unsigned long clk_plle_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val = pll_readl_base(pll);
+ u32 divn = 0, divm = 0, divp = 0;
+ u64 rate = parent_rate;
+
+ divp = (val >> pll->divp_shift) & (divp_mask(pll));
+ divn = (val >> pll->divn_shift) & (divn_mask(pll));
+ divm = (val >> pll->divm_shift) & (divm_mask(pll));
+ divm *= divp;
+
+ rate *= divn;
+ do_div(rate, divm);
+ return rate;
+}
+
+const struct clk_ops tegra_clk_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_pll_set_rate,
+};
+
+const struct clk_ops tegra_clk_plle_ops = {
+ .recalc_rate = clk_plle_recalc_rate,
+ .is_enabled = clk_pll_is_enabled,
+ .disable = clk_pll_disable,
+ .enable = clk_plle_enable,
+};
+
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+
+static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
+ unsigned long parent_rate)
+{
+ if (parent_rate > pll_params->cf_max)
+ return 2;
+ else
+ return 1;
+}
+
+static int clk_pll_iddq_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+
+ u32 val;
+ int ret;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ val = pll_readl(pll->params->iddq_reg, pll);
+ val &= ~BIT(pll->params->iddq_bit_idx);
+ pll_writel(val, pll->params->iddq_reg, pll);
+ udelay(2);
+
+ _clk_pll_enable(hw);
+
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static void clk_pll_iddq_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_disable(hw);
+
+ val = pll_readl(pll->params->iddq_reg, pll);
+ val |= BIT(pll->params->iddq_bit_idx);
+ pll_writel(val, pll->params->iddq_reg, pll);
+ udelay(2);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int _calc_dynamic_ramp_rate(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned int p;
+
+ if (!rate)
+ return -EINVAL;
+
+ p = DIV_ROUND_UP(pll->params->vco_min, rate);
+ cfg->m = _pll_fixed_mdiv(pll->params, parent_rate);
+ cfg->p = p;
+ cfg->output_rate = rate * cfg->p;
+ cfg->n = cfg->output_rate * cfg->m / parent_rate;
+
+ if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int _pll_ramp_calc_pll(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ int err = 0;
+
+ err = _get_table_rate(hw, cfg, rate, parent_rate);
+ if (err < 0)
+ err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate);
+ else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) {
+ WARN_ON(1);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!cfg->p || (cfg->p > pll->params->max_p))
+ err = -EINVAL;
+
+out:
+ return err;
+}
+
+static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ unsigned long flags = 0;
+ int ret = 0;
+ u8 old_p;
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate);
+ if (ret < 0)
+ return ret;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _get_pll_mnp(pll, &old_cfg);
+
+ old_p = pllxc_p[old_cfg.p];
+ if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) {
+ cfg.p -= 1;
+ ret = _program_pll(hw, &cfg, rate);
+ }
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct tegra_clk_pll_freq_table cfg;
+ int ret = 0;
+ u64 output_rate = *prate;
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate);
+ if (ret < 0)
+ return ret;
+
+ output_rate *= cfg.n;
+ do_div(output_rate, cfg.m * cfg.p);
+
+ return output_rate;
+}
+
+static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int state, ret = 0;
+ u32 val;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ state = clk_pll_is_enabled(hw);
+ if (state) {
+ if (rate != clk_get_rate(hw->clk)) {
+ pr_err("%s: Cannot change active PLLM\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+ goto out;
+ }
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate);
+ if (ret < 0)
+ goto out;
+
+ cfg.p -= 1;
+
+ val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE);
+ if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) {
+ val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2);
+ val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) :
+ (val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK);
+ writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2);
+
+ val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE);
+ val &= ~(divn_mask(pll) | divm_mask(pll));
+ val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift);
+ writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE);
+ } else
+ _update_pll_mnp(pll, &cfg);
+
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void _pllcx_strobe(struct tegra_clk_pll *pll)
+{
+ u32 val;
+
+ val = pll_readl_misc(pll);
+ val |= PLLCX_MISC_STROBE;
+ pll_writel_misc(val, pll);
+ udelay(2);
+
+ val &= ~PLLCX_MISC_STROBE;
+ pll_writel_misc(val, pll);
+}
+
+static int clk_pllc_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+ int ret = 0;
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_enable(hw);
+ udelay(2);
+
+ val = pll_readl_misc(pll);
+ val &= ~PLLCX_MISC_RESET;
+ pll_writel_misc(val, pll);
+ udelay(2);
+
+ _pllcx_strobe(pll);
+
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void _clk_pllc_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ _clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val |= PLLCX_MISC_RESET;
+ pll_writel_misc(val, pll);
+ udelay(2);
+}
+
+static void clk_pllc_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pllc_disable(hw);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll,
+ unsigned long input_rate, u32 n)
+{
+ u32 val, n_threshold;
+
+ switch (input_rate) {
+ case 12000000:
+ n_threshold = 70;
+ break;
+ case 13000000:
+ case 26000000:
+ n_threshold = 71;
+ break;
+ case 16800000:
+ n_threshold = 55;
+ break;
+ case 19200000:
+ n_threshold = 48;
+ break;
+ default:
+ pr_err("%s: Unexpected reference rate %lu\n",
+ __func__, input_rate);
+ return -EINVAL;
+ }
+
+ val = pll_readl_misc(pll);
+ val &= ~(PLLCX_MISC_SDM_DIV_MASK | PLLCX_MISC_FILT_DIV_MASK);
+ val |= n <= n_threshold ?
+ PLLCX_MISC_DIV_LOW_RANGE : PLLCX_MISC_DIV_HIGH_RANGE;
+ pll_writel_misc(val, pll);
+
+ return 0;
+}
+
+static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int state, ret = 0;
+ u32 val;
+ u16 old_m, old_n;
+ u8 old_p;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate);
+ if (ret < 0)
+ goto out;
+
+ val = pll_readl_base(pll);
+ old_m = (val >> pll->divm_shift) & (divm_mask(pll));
+ old_n = (val >> pll->divn_shift) & (divn_mask(pll));
+ old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))];
+
+ if (cfg.m != old_m) {
+ WARN_ON(1);
+ goto out;
+ }
+
+ if (old_n == cfg.n && old_p == cfg.p)
+ goto out;
+
+ cfg.p -= 1;
+
+ state = clk_pll_is_enabled(hw);
+ if (state)
+ _clk_pllc_disable(hw);
+
+ ret = _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n);
+ if (ret < 0)
+ goto out;
+
+ _update_pll_mnp(pll, &cfg);
+
+ if (state)
+ ret = clk_pllc_enable(hw);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static long _pllre_calc_rate(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ u16 m, n;
+ u64 output_rate = parent_rate;
+
+ m = _pll_fixed_mdiv(pll->params, parent_rate);
+ n = rate * m / parent_rate;
+
+ output_rate *= n;
+ do_div(output_rate, m);
+
+ if (cfg) {
+ cfg->m = m;
+ cfg->n = n;
+ }
+
+ return output_rate;
+}
+static int clk_pllre_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int state, ret = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _pllre_calc_rate(pll, &cfg, rate, parent_rate);
+ _get_pll_mnp(pll, &old_cfg);
+ cfg.p = old_cfg.p;
+
+ if (cfg.m != old_cfg.m || cfg.n != old_cfg.n) {
+ state = clk_pll_is_enabled(hw);
+ if (state)
+ _clk_pll_disable(hw);
+
+ _update_pll_mnp(pll, &cfg);
+
+ if (state) {
+ _clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll);
+ }
+ }
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static unsigned long clk_pllre_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u64 rate = parent_rate;
+
+ _get_pll_mnp(pll, &cfg);
+
+ rate *= cfg.n;
+ do_div(rate, cfg.m);
+
+ return rate;
+}
+
+static long clk_pllre_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+
+ return _pllre_calc_rate(pll, NULL, rate, *prate);
+}
+
+static int clk_plle_tegra114_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table sel;
+ u32 val;
+ int ret;
+ unsigned long flags = 0;
+ unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk));
+
+ if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
+ return -EINVAL;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ val = pll_readl_base(pll);
+ val &= ~BIT(29); /* Disable lock override */
+ pll_writel_base(val, pll);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ val |= PLLE_AUX_ENABLE_SWCTL;
+ val &= ~PLLE_AUX_SEQ_ENABLE;
+ pll_writel(val, pll->params->aux_reg, pll);
+ udelay(1);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_LOCK_ENABLE;
+ val |= PLLE_MISC_IDDQ_SW_CTRL;
+ val &= ~PLLE_MISC_IDDQ_SW_VALUE;
+ val |= PLLE_MISC_PLLE_PTS;
+ val |= PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK;
+ pll_writel_misc(val, pll);
+ udelay(5);
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val |= PLLE_SS_DISABLE;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+
+ val = pll_readl_base(pll);
+ val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll));
+ val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << pll->divm_shift;
+ val |= sel.n << pll->divn_shift;
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+ udelay(1);
+
+ _clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (ret < 0)
+ goto out;
+
+ /* TODO: enable hw control of xusb brick pll */
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void clk_plle_tegra114_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE;
+ pll_writel_misc(val, pll);
+ udelay(1);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+#endif
+
+static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
+ void __iomem *pmc, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->clk_base = clk_base;
+ pll->pmc = pmc;
+
+ pll->freq_table = freq_table;
+ pll->params = pll_params;
+ pll->fixed_rate = fixed_rate;
+ pll->flags = pll_flags;
+ pll->lock = lock;
+
+ pll->divp_shift = PLL_BASE_DIVP_SHIFT;
+ pll->divp_width = PLL_BASE_DIVP_WIDTH;
+ pll->divn_shift = PLL_BASE_DIVN_SHIFT;
+ pll->divn_width = PLL_BASE_DIVN_WIDTH;
+ pll->divm_shift = PLL_BASE_DIVM_SHIFT;
+ pll->divm_width = PLL_BASE_DIVM_WIDTH;
+
+ return pll;
+}
+
+static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll,
+ const char *name, const char *parent_name, unsigned long flags,
+ const struct clk_ops *ops)
+{
+ struct clk_init_data init;
+
+ init.name = name;
+ init.ops = ops;
+ init.flags = flags;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ pll->hw.init = &init;
+
+ return clk_register(NULL, &pll->hw);
+}
+
+struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_flags |= TEGRA_PLL_BYPASS;
+ pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
+ pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
+ freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS;
+ pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
+ pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
+ freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_plle_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+const struct clk_ops tegra_clk_pllxc_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_iddq_enable,
+ .disable = clk_pll_iddq_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_ramp_round_rate,
+ .set_rate = clk_pllxc_set_rate,
+};
+
+const struct clk_ops tegra_clk_pllm_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_iddq_enable,
+ .disable = clk_pll_iddq_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_ramp_round_rate,
+ .set_rate = clk_pllm_set_rate,
+};
+
+const struct clk_ops tegra_clk_pllc_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pllc_enable,
+ .disable = clk_pllc_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_ramp_round_rate,
+ .set_rate = clk_pllc_set_rate,
+};
+
+const struct clk_ops tegra_clk_pllre_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_iddq_enable,
+ .disable = clk_pll_iddq_disable,
+ .recalc_rate = clk_pllre_recalc_rate,
+ .round_rate = clk_pllre_round_rate,
+ .set_rate = clk_pllre_set_rate,
+};
+
+const struct clk_ops tegra_clk_plle_tegra114_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_plle_tegra114_enable,
+ .disable = clk_plle_tegra114_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+
+struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ if (!pll_params->pdiv_tohw)
+ return ERR_PTR(-EINVAL);
+
+ pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
+ pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
+ freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllxc_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock, unsigned long parent_rate)
+{
+ u32 val;
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
+ pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
+ freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ /* program minimum rate by default */
+
+ val = pll_readl_base(pll);
+ if (val & PLL_BASE_ENABLE)
+ WARN_ON(val & pll_params->iddq_bit_idx);
+ else {
+ int m;
+
+ m = _pll_fixed_mdiv(pll_params, parent_rate);
+ val = m << PLL_BASE_DIVM_SHIFT;
+ val |= (pll_params->vco_min / parent_rate)
+ << PLL_BASE_DIVN_SHIFT;
+ pll_writel_base(val, pll);
+ }
+
+ /* disable lock override */
+
+ val = pll_readl_misc(pll);
+ val &= ~BIT(29);
+ pll_writel_misc(val, pll);
+
+ pll_flags |= TEGRA_PLL_LOCK_MISC;
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllre_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ if (!pll_params->pdiv_tohw)
+ return ERR_PTR(-EINVAL);
+
+ pll_flags |= TEGRA_PLL_BYPASS;
+ pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
+ pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
+ freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllm_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock)
+{
+ struct clk *parent, *clk;
+ struct pdiv_map *p_tohw = pll_params->pdiv_tohw;
+ struct tegra_clk_pll *pll;
+ struct tegra_clk_pll_freq_table cfg;
+ unsigned long parent_rate;
+
+ if (!p_tohw)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (IS_ERR(parent)) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ name, parent_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pll_flags |= TEGRA_PLL_BYPASS;
+ pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
+ freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ parent_rate = __clk_get_rate(parent);
+
+ /*
+ * Most of PLLC register fields are shadowed, and can not be read
+ * directly from PLL h/w. Hence, actual PLLC boot state is unknown.
+ * Initialize PLL to default state: disabled, reset; shadow registers
+ * loaded with default parameters; dividers are preset for half of
+ * minimum VCO rate (the latter assured that shadowed divider settings
+ * are within supported range).
+ */
+
+ cfg.m = _pll_fixed_mdiv(pll_params, parent_rate);
+ cfg.n = cfg.m * pll_params->vco_min / parent_rate;
+
+ while (p_tohw->pdiv) {
+ if (p_tohw->pdiv == 2) {
+ cfg.p = p_tohw->hw_val;
+ break;
+ }
+ p_tohw++;
+ }
+
+ if (!p_tohw->pdiv) {
+ WARN_ON(1);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pll_writel_base(0, pll);
+ _update_pll_mnp(pll, &cfg);
+
+ pll_writel_misc(PLLCX_MISC_DEFAULT, pll);
+ pll_writel(PLLCX_MISC1_DEFAULT, pll_params->ext_misc_reg[0], pll);
+ pll_writel(PLLCX_MISC2_DEFAULT, pll_params->ext_misc_reg[1], pll);
+ pll_writel(PLLCX_MISC3_DEFAULT, pll_params->ext_misc_reg[2], pll);
+
+ _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllc_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_plle_tegra114(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+ u32 val, val_aux;
+
+ pll = _tegra_init_pll(clk_base, NULL, fixed_rate, pll_params,
+ TEGRA_PLL_HAS_LOCK_ENABLE, freq_table, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ /* ensure parent is set to pll_re_vco */
+
+ val = pll_readl_base(pll);
+ val_aux = pll_readl(pll_params->aux_reg, pll);
+
+ if (val & PLL_BASE_ENABLE) {
+ if (!(val_aux & PLLE_AUX_PLLRE_SEL))
+ WARN(1, "pll_e enabled with unsupported parent %s\n",
+ (val & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : "pll_ref");
+ } else {
+ val_aux |= PLLE_AUX_PLLRE_SEL;
+ pll_writel(val, pll_params->aux_reg, pll);
+ }
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_plle_tegra114_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+#endif
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
new file mode 100644
index 000000000..2fd924d38
--- /dev/null
+++ b/drivers/clk/tegra/clk-super.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+
+#include "clk.h"
+
+#define SUPER_STATE_IDLE 0
+#define SUPER_STATE_RUN 1
+#define SUPER_STATE_IRQ 2
+#define SUPER_STATE_FIQ 3
+
+#define SUPER_STATE_SHIFT 28
+#define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \
+ BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \
+ << SUPER_STATE_SHIFT)
+
+#define SUPER_LP_DIV2_BYPASS (1 << 16)
+
+#define super_state(s) (BIT(s) << SUPER_STATE_SHIFT)
+#define super_state_to_src_shift(m, s) ((m->width * s))
+#define super_state_to_src_mask(m) (((1 << m->width) - 1))
+
+static u8 clk_super_get_parent(struct clk_hw *hw)
+{
+ struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
+ u32 val, state;
+ u8 source, shift;
+
+ val = readl_relaxed(mux->reg);
+
+ state = val & SUPER_STATE_MASK;
+
+ BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
+ (state != super_state(SUPER_STATE_IDLE)));
+ shift = (state == super_state(SUPER_STATE_IDLE)) ?
+ super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
+ super_state_to_src_shift(mux, SUPER_STATE_RUN);
+
+ source = (val >> shift) & super_state_to_src_mask(mux);
+
+ /*
+ * If LP_DIV2_BYPASS is not set and PLLX is current parent then
+ * PLLX/2 is the input source to CCLKLP.
+ */
+ if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) &&
+ (source == mux->pllx_index))
+ source = mux->div2_index;
+
+ return source;
+}
+
+static int clk_super_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
+ u32 val, state;
+ int err = 0;
+ u8 parent_index, shift;
+ unsigned long flags = 0;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+ val = readl_relaxed(mux->reg);
+ state = val & SUPER_STATE_MASK;
+ BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
+ (state != super_state(SUPER_STATE_IDLE)));
+ shift = (state == super_state(SUPER_STATE_IDLE)) ?
+ super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
+ super_state_to_src_shift(mux, SUPER_STATE_RUN);
+
+ /*
+ * For LP mode super-clock switch between PLLX direct
+ * and divided-by-2 outputs is allowed only when other
+ * than PLLX clock source is current parent.
+ */
+ if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) ||
+ (index == mux->pllx_index))) {
+ parent_index = clk_super_get_parent(hw);
+ if ((parent_index == mux->div2_index) ||
+ (parent_index == mux->pllx_index)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ val ^= SUPER_LP_DIV2_BYPASS;
+ writel_relaxed(val, mux->reg);
+ udelay(2);
+
+ if (index == mux->div2_index)
+ index = mux->pllx_index;
+ }
+ val &= ~((super_state_to_src_mask(mux)) << shift);
+ val |= (index & (super_state_to_src_mask(mux))) << shift;
+
+ writel_relaxed(val, mux->reg);
+ udelay(2);
+
+out:
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return err;
+}
+
+const struct clk_ops tegra_clk_super_ops = {
+ .get_parent = clk_super_get_parent,
+ .set_parent = clk_super_set_parent,
+};
+
+struct clk *tegra_clk_register_super_mux(const char *name,
+ const char **parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock)
+{
+ struct tegra_clk_super_mux *super;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ super = kzalloc(sizeof(*super), GFP_KERNEL);
+ if (!super) {
+ pr_err("%s: could not allocate super clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &tegra_clk_super_ops;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ super->reg = reg;
+ super->pllx_index = pllx_index;
+ super->div2_index = div2_index;
+ super->lock = lock;
+ super->width = width;
+ super->flags = clk_super_flags;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ super->hw.init = &init;
+
+ clk = clk_register(NULL, &super->hw);
+ if (IS_ERR(clk))
+ kfree(super);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
new file mode 100644
index 000000000..d78e16ee1
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -0,0 +1,2085 @@
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/clk/tegra.h>
+
+#include "clk.h"
+
+#define RST_DEVICES_L 0x004
+#define RST_DEVICES_H 0x008
+#define RST_DEVICES_U 0x00C
+#define RST_DEVICES_V 0x358
+#define RST_DEVICES_W 0x35C
+#define RST_DEVICES_X 0x28C
+#define RST_DEVICES_SET_L 0x300
+#define RST_DEVICES_CLR_L 0x304
+#define RST_DEVICES_SET_H 0x308
+#define RST_DEVICES_CLR_H 0x30c
+#define RST_DEVICES_SET_U 0x310
+#define RST_DEVICES_CLR_U 0x314
+#define RST_DEVICES_SET_V 0x430
+#define RST_DEVICES_CLR_V 0x434
+#define RST_DEVICES_SET_W 0x438
+#define RST_DEVICES_CLR_W 0x43c
+#define RST_DEVICES_NUM 5
+
+#define CLK_OUT_ENB_L 0x010
+#define CLK_OUT_ENB_H 0x014
+#define CLK_OUT_ENB_U 0x018
+#define CLK_OUT_ENB_V 0x360
+#define CLK_OUT_ENB_W 0x364
+#define CLK_OUT_ENB_X 0x280
+#define CLK_OUT_ENB_SET_L 0x320
+#define CLK_OUT_ENB_CLR_L 0x324
+#define CLK_OUT_ENB_SET_H 0x328
+#define CLK_OUT_ENB_CLR_H 0x32c
+#define CLK_OUT_ENB_SET_U 0x330
+#define CLK_OUT_ENB_CLR_U 0x334
+#define CLK_OUT_ENB_SET_V 0x440
+#define CLK_OUT_ENB_CLR_V 0x444
+#define CLK_OUT_ENB_SET_W 0x448
+#define CLK_OUT_ENB_CLR_W 0x44c
+#define CLK_OUT_ENB_SET_X 0x284
+#define CLK_OUT_ENB_CLR_X 0x288
+#define CLK_OUT_ENB_NUM 6
+
+#define PLLC_BASE 0x80
+#define PLLC_MISC2 0x88
+#define PLLC_MISC 0x8c
+#define PLLC2_BASE 0x4e8
+#define PLLC2_MISC 0x4ec
+#define PLLC3_BASE 0x4fc
+#define PLLC3_MISC 0x500
+#define PLLM_BASE 0x90
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLX_MISC2 0x514
+#define PLLX_MISC3 0x518
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC 0x4bc
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+#define PLLRE_BASE 0x4c4
+#define PLLRE_MISC 0x4c8
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLC_MISC_LOCK_ENABLE 24
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+#define PLLRE_MISC_LOCK_ENABLE 30
+
+#define PLLC_IDDQ_BIT 26
+#define PLLX_IDDQ_BIT 3
+#define PLLRE_IDDQ_BIT 16
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+#define PLLRE_MISC_LOCK BIT(24)
+#define PLLCX_BASE_LOCK (BIT(26)|BIT(27))
+
+#define PLLE_AUX 0x48c
+#define PLLC_OUT 0x84
+#define PLLM_OUT 0x94
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
+#define PLLA_OUT 0xb4
+
+#define AUDIO_SYNC_CLK_I2S0 0x4a0
+#define AUDIO_SYNC_CLK_I2S1 0x4a4
+#define AUDIO_SYNC_CLK_I2S2 0x4a8
+#define AUDIO_SYNC_CLK_I2S3 0x4ac
+#define AUDIO_SYNC_CLK_I2S4 0x4b0
+#define AUDIO_SYNC_CLK_SPDIF 0x4b4
+
+#define AUDIO_SYNC_DOUBLER 0x49c
+
+#define PMC_CLK_OUT_CNTRL 0x1a8
+#define PMC_DPD_PADS_ORIDE 0x1c
+#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20
+#define PMC_CTRL 0
+#define PMC_CTRL_BLINK_ENB 7
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_SHIFT 28
+#define OSC_CTRL_PLL_REF_DIV_SHIFT 26
+
+#define PLLXC_SW_MAX_P 6
+
+#define CCLKG_BURST_POLICY 0x368
+#define CCLKLP_BURST_POLICY 0x370
+#define SCLK_BURST_POLICY 0x028
+#define SYSTEM_CLK_RATE 0x030
+
+#define UTMIP_PLL_CFG2 0x488
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6)
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4)
+
+#define UTMIP_PLL_CFG1 0x484
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6)
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12)
+
+#define UTMIPLL_HW_PWRDN_CFG0 0x52c
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
+#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4)
+#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0)
+
+#define CLK_SOURCE_I2S0 0x1d8
+#define CLK_SOURCE_I2S1 0x100
+#define CLK_SOURCE_I2S2 0x104
+#define CLK_SOURCE_NDFLASH 0x160
+#define CLK_SOURCE_I2S3 0x3bc
+#define CLK_SOURCE_I2S4 0x3c0
+#define CLK_SOURCE_SPDIF_OUT 0x108
+#define CLK_SOURCE_SPDIF_IN 0x10c
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_ADX 0x638
+#define CLK_SOURCE_AMX 0x63c
+#define CLK_SOURCE_HDA 0x428
+#define CLK_SOURCE_HDA2CODEC_2X 0x3e4
+#define CLK_SOURCE_SBC1 0x134
+#define CLK_SOURCE_SBC2 0x118
+#define CLK_SOURCE_SBC3 0x11c
+#define CLK_SOURCE_SBC4 0x1b4
+#define CLK_SOURCE_SBC5 0x3c8
+#define CLK_SOURCE_SBC6 0x3cc
+#define CLK_SOURCE_SATA_OOB 0x420
+#define CLK_SOURCE_SATA 0x424
+#define CLK_SOURCE_NDSPEED 0x3f8
+#define CLK_SOURCE_VFIR 0x168
+#define CLK_SOURCE_SDMMC1 0x150
+#define CLK_SOURCE_SDMMC2 0x154
+#define CLK_SOURCE_SDMMC3 0x1bc
+#define CLK_SOURCE_SDMMC4 0x164
+#define CLK_SOURCE_VDE 0x1c8
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_LA 0x1f8
+#define CLK_SOURCE_TRACE 0x634
+#define CLK_SOURCE_OWR 0x1cc
+#define CLK_SOURCE_NOR 0x1d0
+#define CLK_SOURCE_MIPI 0x174
+#define CLK_SOURCE_I2C1 0x124
+#define CLK_SOURCE_I2C2 0x198
+#define CLK_SOURCE_I2C3 0x1b8
+#define CLK_SOURCE_I2C4 0x3c4
+#define CLK_SOURCE_I2C5 0x128
+#define CLK_SOURCE_UARTA 0x178
+#define CLK_SOURCE_UARTB 0x17c
+#define CLK_SOURCE_UARTC 0x1a0
+#define CLK_SOURCE_UARTD 0x1c0
+#define CLK_SOURCE_UARTE 0x1c4
+#define CLK_SOURCE_UARTA_DBG 0x178
+#define CLK_SOURCE_UARTB_DBG 0x17c
+#define CLK_SOURCE_UARTC_DBG 0x1a0
+#define CLK_SOURCE_UARTD_DBG 0x1c0
+#define CLK_SOURCE_UARTE_DBG 0x1c4
+#define CLK_SOURCE_3D 0x158
+#define CLK_SOURCE_2D 0x15c
+#define CLK_SOURCE_VI_SENSOR 0x1a8
+#define CLK_SOURCE_VI 0x148
+#define CLK_SOURCE_EPP 0x16c
+#define CLK_SOURCE_MSENC 0x1f0
+#define CLK_SOURCE_TSEC 0x1f4
+#define CLK_SOURCE_HOST1X 0x180
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_DISP1 0x138
+#define CLK_SOURCE_DISP2 0x13c
+#define CLK_SOURCE_CILAB 0x614
+#define CLK_SOURCE_CILCD 0x618
+#define CLK_SOURCE_CILE 0x61c
+#define CLK_SOURCE_DSIALP 0x620
+#define CLK_SOURCE_DSIBLP 0x624
+#define CLK_SOURCE_TSENSOR 0x3b8
+#define CLK_SOURCE_D_AUDIO 0x3d0
+#define CLK_SOURCE_DAM0 0x3d8
+#define CLK_SOURCE_DAM1 0x3dc
+#define CLK_SOURCE_DAM2 0x3e0
+#define CLK_SOURCE_ACTMON 0x3e8
+#define CLK_SOURCE_EXTERN1 0x3ec
+#define CLK_SOURCE_EXTERN2 0x3f0
+#define CLK_SOURCE_EXTERN3 0x3f4
+#define CLK_SOURCE_I2CSLOW 0x3fc
+#define CLK_SOURCE_SE 0x42c
+#define CLK_SOURCE_MSELECT 0x3b4
+#define CLK_SOURCE_SOC_THERM 0x644
+#define CLK_SOURCE_XUSB_HOST_SRC 0x600
+#define CLK_SOURCE_XUSB_FALCON_SRC 0x604
+#define CLK_SOURCE_XUSB_FS_SRC 0x608
+#define CLK_SOURCE_XUSB_SS_SRC 0x610
+#define CLK_SOURCE_XUSB_DEV_SRC 0x60c
+#define CLK_SOURCE_EMC 0x19c
+
+static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32];
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+
+static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(pll_d2_lock);
+static DEFINE_SPINLOCK(pll_u_lock);
+static DEFINE_SPINLOCK(pll_div_lock);
+static DEFINE_SPINLOCK(pll_re_lock);
+static DEFINE_SPINLOCK(clk_doubler_lock);
+static DEFINE_SPINLOCK(clk_out_lock);
+static DEFINE_SPINLOCK(sysrate_lock);
+
+static struct pdiv_map pllxc_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 624000000, 104, 0, 2},
+ { 12000000, 600000000, 100, 0, 2},
+ { 13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 600000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLC_MISC,
+ .iddq_bit_idx = PLLC_IDDQ_BIT,
+ .max_p = PLLXC_SW_MAX_P,
+ .dyn_ramp_reg = PLLC_MISC2,
+ .stepa_shift = 17,
+ .stepb_shift = 9,
+ .pdiv_tohw = pllxc_p,
+};
+
+static struct pdiv_map pllc_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 8, .hw_val = 5 },
+ { .pdiv = 16, .hw_val = 7 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = {
+ {12000000, 600000000, 100, 0, 2},
+ {13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */
+ {16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */
+ {19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */
+ {26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_c2_params = {
+ .input_min = 12000000,
+ .input_max = 48000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC2_BASE,
+ .misc_reg = PLLC2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = pllc_p,
+ .ext_misc_reg[0] = 0x4f0,
+ .ext_misc_reg[1] = 0x4f4,
+ .ext_misc_reg[2] = 0x4f8,
+};
+
+static struct tegra_clk_pll_params pll_c3_params = {
+ .input_min = 12000000,
+ .input_max = 48000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC3_BASE,
+ .misc_reg = PLLC3_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = pllc_p,
+ .ext_misc_reg[0] = 0x504,
+ .ext_misc_reg[1] = 0x508,
+ .ext_misc_reg[2] = 0x50c,
+};
+
+static struct pdiv_map pllm_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ {12000000, 800000000, 66, 0, 1}, /* actual: 792.0 MHz */
+ {13000000, 800000000, 61, 0, 1}, /* actual: 793.0 MHz */
+ {16800000, 800000000, 47, 0, 1}, /* actual: 789.6 MHz */
+ {19200000, 800000000, 41, 0, 1}, /* actual: 787.2 MHz */
+ {26000000, 800000000, 61, 1, 1}, /* actual: 793.0 MHz */
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 12000000,
+ .input_max = 500000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 400000000,
+ .vco_max = 1066000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .max_p = 2,
+ .pdiv_tohw = pllm_p,
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ {12000000, 216000000, 432, 12, 1, 8},
+ {13000000, 216000000, 432, 13, 1, 8},
+ {16800000, 216000000, 360, 14, 1, 8},
+ {19200000, 216000000, 360, 16, 1, 8},
+ {26000000, 216000000, 432, 26, 1, 8},
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 200000000,
+ .vco_max = 700000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ {9600000, 282240000, 147, 5, 0, 4},
+ {9600000, 368640000, 192, 5, 0, 4},
+ {9600000, 240000000, 200, 8, 0, 8},
+
+ {28800000, 282240000, 245, 25, 0, 8},
+ {28800000, 368640000, 320, 25, 0, 8},
+ {28800000, 240000000, 200, 24, 0, 8},
+ {0, 0, 0, 0, 0, 0},
+};
+
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 200000000,
+ .vco_max = 700000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ {12000000, 216000000, 864, 12, 2, 12},
+ {13000000, 216000000, 864, 13, 2, 12},
+ {16800000, 216000000, 720, 14, 2, 12},
+ {19200000, 216000000, 720, 16, 2, 12},
+ {26000000, 216000000, 864, 26, 2, 12},
+
+ {12000000, 594000000, 594, 12, 0, 12},
+ {13000000, 594000000, 594, 13, 0, 12},
+ {16800000, 594000000, 495, 14, 0, 12},
+ {19200000, 594000000, 495, 16, 0, 12},
+ {26000000, 594000000, 594, 26, 0, 12},
+
+ {12000000, 1000000000, 1000, 12, 0, 12},
+ {13000000, 1000000000, 1000, 13, 0, 12},
+ {19200000, 1000000000, 625, 12, 0, 12},
+ {26000000, 1000000000, 1000, 26, 0, 12},
+
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 500000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+};
+
+static struct tegra_clk_pll_params pll_d2_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 500000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+};
+
+static struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ {12000000, 480000000, 960, 12, 0, 12},
+ {13000000, 480000000, 960, 13, 0, 12},
+ {16800000, 480000000, 400, 7, 0, 5},
+ {19200000, 480000000, 200, 4, 0, 3},
+ {26000000, 480000000, 960, 26, 0, 12},
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_u_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 480000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1 GHz */
+ {12000000, 1000000000, 83, 0, 1}, /* actual: 996.0 MHz */
+ {13000000, 1000000000, 76, 0, 1}, /* actual: 988.0 MHz */
+ {16800000, 1000000000, 59, 0, 1}, /* actual: 991.2 MHz */
+ {19200000, 1000000000, 52, 0, 1}, /* actual: 998.4 MHz */
+ {26000000, 1000000000, 76, 1, 1}, /* actual: 988.0 MHz */
+
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 700000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLX_MISC3,
+ .iddq_bit_idx = PLLX_IDDQ_BIT,
+ .max_p = PLLXC_SW_MAX_P,
+ .dyn_ramp_reg = PLLX_MISC2,
+ .stepa_shift = 16,
+ .stepb_shift = 24,
+ .pdiv_tohw = pllxc_p,
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ {336000000, 100000000, 100, 21, 16, 11},
+ {312000000, 100000000, 200, 26, 24, 13},
+ {0, 0, 0, 0, 0, 0},
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 75000000,
+ .vco_min = 1600000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .aux_reg = PLLE_AUX,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_re_vco_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */
+ .vco_min = 300000000,
+ .vco_max = 600000000,
+ .base_reg = PLLRE_BASE,
+ .misc_reg = PLLRE_MISC,
+ .lock_mask = PLLRE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLRE_MISC,
+ .iddq_bit_idx = PLLRE_IDDQ_BIT,
+};
+
+/* Peripheral clock registers */
+
+static struct tegra_clk_periph_regs periph_l_regs = {
+ .enb_reg = CLK_OUT_ENB_L,
+ .enb_set_reg = CLK_OUT_ENB_SET_L,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_L,
+ .rst_reg = RST_DEVICES_L,
+ .rst_set_reg = RST_DEVICES_SET_L,
+ .rst_clr_reg = RST_DEVICES_CLR_L,
+};
+
+static struct tegra_clk_periph_regs periph_h_regs = {
+ .enb_reg = CLK_OUT_ENB_H,
+ .enb_set_reg = CLK_OUT_ENB_SET_H,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_H,
+ .rst_reg = RST_DEVICES_H,
+ .rst_set_reg = RST_DEVICES_SET_H,
+ .rst_clr_reg = RST_DEVICES_CLR_H,
+};
+
+static struct tegra_clk_periph_regs periph_u_regs = {
+ .enb_reg = CLK_OUT_ENB_U,
+ .enb_set_reg = CLK_OUT_ENB_SET_U,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_U,
+ .rst_reg = RST_DEVICES_U,
+ .rst_set_reg = RST_DEVICES_SET_U,
+ .rst_clr_reg = RST_DEVICES_CLR_U,
+};
+
+static struct tegra_clk_periph_regs periph_v_regs = {
+ .enb_reg = CLK_OUT_ENB_V,
+ .enb_set_reg = CLK_OUT_ENB_SET_V,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_V,
+ .rst_reg = RST_DEVICES_V,
+ .rst_set_reg = RST_DEVICES_SET_V,
+ .rst_clr_reg = RST_DEVICES_CLR_V,
+};
+
+static struct tegra_clk_periph_regs periph_w_regs = {
+ .enb_reg = CLK_OUT_ENB_W,
+ .enb_set_reg = CLK_OUT_ENB_SET_W,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_W,
+ .rst_reg = RST_DEVICES_W,
+ .rst_set_reg = RST_DEVICES_SET_W,
+ .rst_clr_reg = RST_DEVICES_CLR_W,
+};
+
+/* possible OSC frequencies in Hz */
+static unsigned long tegra114_input_freq[] = {
+ [0] = 13000000,
+ [1] = 16800000,
+ [4] = 19200000,
+ [5] = 38400000,
+ [8] = 12000000,
+ [9] = 48000000,
+ [12] = 260000000,
+};
+
+#define MASK(x) (BIT(x) - 1)
+
+#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, _gate_flags, _clk_id, \
+ _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_MUX_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\
+ _clk_num, _regs, _gate_flags, _clk_id, flags)\
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, _gate_flags, _clk_id, \
+ _parents##_idx, flags)
+
+#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 29, MASK(3), 0, 0, 8, 1, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, _gate_flags, _clk_id, \
+ _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id, _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_INT_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\
+ _clk_num, _regs, _gate_flags, _clk_id, flags)\
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id, _parents##_idx, flags)
+
+#define TEGRA_INIT_DATA_INT8(_name, _con_id, _dev_id, _parents, _offset,\
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id, _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\
+ _clk_num, _regs, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs,\
+ _clk_num, periph_clk_enb_refcnt, 0, _clk_id, \
+ _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_I2C(_name, _con_id, _dev_id, _parents, _offset,\
+ _clk_num, _regs, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ 30, MASK(2), 0, 0, 16, 0, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, 0, _clk_id, _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \
+ _mux_shift, _mux_mask, _clk_num, _regs, \
+ _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\
+ _mux_shift, _mux_mask, 0, 0, 0, 0, 0, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id, _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_XUSB(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset, \
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id, _parents##_idx, 0)
+
+#define TEGRA_INIT_DATA_AUDIO(_name, _con_id, _dev_id, _offset, _clk_num,\
+ _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, mux_d_audio_clk, \
+ _offset, 16, 0xE01F, 0, 0, 8, 1, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, _gate_flags , _clk_id, \
+ mux_d_audio_clk_idx, 0)
+
+enum tegra114_clk {
+ rtc = 4, timer = 5, uarta = 6, sdmmc2 = 9, i2s1 = 11, i2c1 = 12,
+ ndflash = 13, sdmmc1 = 14, sdmmc4 = 15, pwm = 17, i2s2 = 18, epp = 19,
+ gr_2d = 21, usbd = 22, isp = 23, gr_3d = 24, disp2 = 26, disp1 = 27,
+ host1x = 28, vcp = 29, i2s0 = 30, apbdma = 34, kbc = 36, kfuse = 40,
+ sbc1 = 41, nor = 42, sbc2 = 44, sbc3 = 46, i2c5 = 47, dsia = 48,
+ mipi = 50, hdmi = 51, csi = 52, i2c2 = 54, uartc = 55, mipi_cal = 56,
+ emc, usb2, usb3, vde = 61, bsea = 62, bsev = 63, uartd = 65,
+ i2c3 = 67, sbc4 = 68, sdmmc3 = 69, owr = 71, csite = 73,
+ la = 76, trace = 77, soc_therm = 78, dtv = 79, ndspeed = 80,
+ i2cslow = 81, dsib = 82, tsec = 83, xusb_host = 89, msenc = 91,
+ csus = 92, mselect = 99, tsensor = 100, i2s3 = 101, i2s4 = 102,
+ i2c4 = 103, sbc5 = 104, sbc6 = 105, d_audio, apbif = 107, dam0, dam1,
+ dam2, hda2codec_2x = 111, audio0_2x = 113, audio1_2x, audio2_2x,
+ audio3_2x, audio4_2x, spdif_2x, actmon = 119, extern1 = 120,
+ extern2 = 121, extern3 = 122, hda = 125, se = 127, hda2hdmi = 128,
+ cilab = 144, cilcd = 145, cile = 146, dsialp = 147, dsiblp = 148,
+ dds = 150, dp2 = 152, amx = 153, adx = 154, xusb_ss = 156, uartb = 192,
+ vfir, spdif_in, spdif_out, vi, vi_sensor, fuse, fuse_burn, clk_32k,
+ clk_m, clk_m_div2, clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_c2,
+ pll_c3, pll_m, pll_m_out1, pll_p, pll_p_out1, pll_p_out2, pll_p_out3,
+ pll_p_out4, pll_a, pll_a_out0, pll_d, pll_d_out0, pll_d2, pll_d2_out0,
+ pll_u, pll_u_480M, pll_u_60M, pll_u_48M, pll_u_12M, pll_x, pll_x_out0,
+ pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync,
+ i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0,
+ audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3,
+ blink, xusb_host_src = 252, xusb_falcon_src, xusb_fs_src, xusb_ss_src,
+ xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp,
+
+ /* Mux clocks */
+
+ audio0_mux = 300, audio1_mux, audio2_mux, audio3_mux, audio4_mux,
+ spdif_mux, clk_out_1_mux, clk_out_2_mux, clk_out_3_mux, dsia_mux,
+ dsib_mux, clk_max,
+};
+
+struct utmi_clk_param {
+ /* Oscillator Frequency in KHz */
+ u32 osc_frequency;
+ /* UTMIP PLL Enable Delay Count */
+ u8 enable_delay_count;
+ /* UTMIP PLL Stable count */
+ u8 stable_count;
+ /* UTMIP PLL Active delay count */
+ u8 active_delay_count;
+ /* UTMIP PLL Xtal frequency count */
+ u8 xtal_freq_count;
+};
+
+static const struct utmi_clk_param utmi_parameters[] = {
+ {.osc_frequency = 13000000, .enable_delay_count = 0x02,
+ .stable_count = 0x33, .active_delay_count = 0x05,
+ .xtal_freq_count = 0x7F},
+ {.osc_frequency = 19200000, .enable_delay_count = 0x03,
+ .stable_count = 0x4B, .active_delay_count = 0x06,
+ .xtal_freq_count = 0xBB},
+ {.osc_frequency = 12000000, .enable_delay_count = 0x02,
+ .stable_count = 0x2F, .active_delay_count = 0x04,
+ .xtal_freq_count = 0x76},
+ {.osc_frequency = 26000000, .enable_delay_count = 0x04,
+ .stable_count = 0x66, .active_delay_count = 0x09,
+ .xtal_freq_count = 0xFE},
+ {.osc_frequency = 16800000, .enable_delay_count = 0x03,
+ .stable_count = 0x41, .active_delay_count = 0x0A,
+ .xtal_freq_count = 0xA4},
+};
+
+/* peripheral mux definitions */
+
+#define MUX_I2S_SPDIF(_id) \
+static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \
+ #_id, "pll_p",\
+ "clk_m"};
+MUX_I2S_SPDIF(audio0)
+MUX_I2S_SPDIF(audio1)
+MUX_I2S_SPDIF(audio2)
+MUX_I2S_SPDIF(audio3)
+MUX_I2S_SPDIF(audio4)
+MUX_I2S_SPDIF(audio)
+
+#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL
+
+static const char *mux_pllp_pllc_pllm_clkm[] = {
+ "pll_p", "pll_c", "pll_m", "clk_m"
+};
+#define mux_pllp_pllc_pllm_clkm_idx NULL
+
+static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" };
+#define mux_pllp_pllc_pllm_idx NULL
+
+static const char *mux_pllp_pllc_clk32_clkm[] = {
+ "pll_p", "pll_c", "clk_32k", "clk_m"
+};
+#define mux_pllp_pllc_clk32_clkm_idx NULL
+
+static const char *mux_plla_pllc_pllp_clkm[] = {
+ "pll_a_out0", "pll_c", "pll_p", "clk_m"
+};
+#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx
+
+static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = {
+ "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m"
+};
+static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6,
+};
+
+static const char *mux_pllp_clkm[] = {
+ "pll_p", "clk_m"
+};
+static u32 mux_pllp_clkm_idx[] = {
+ [0] = 0, [1] = 3,
+};
+
+static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = {
+ "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0"
+};
+#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx
+
+static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = {
+ "pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c",
+ "pll_d2_out0", "clk_m"
+};
+#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL
+
+static const char *mux_pllm_pllc_pllp_plla[] = {
+ "pll_m", "pll_c", "pll_p", "pll_a_out0"
+};
+#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx
+
+static const char *mux_pllp_pllc_clkm[] = {
+ "pll_p", "pll_c", "pll_m"
+};
+static u32 mux_pllp_pllc_clkm_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3,
+};
+
+static const char *mux_pllp_pllc_clkm_clk32[] = {
+ "pll_p", "pll_c", "clk_m", "clk_32k"
+};
+#define mux_pllp_pllc_clkm_clk32_idx NULL
+
+static const char *mux_plla_clk32_pllp_clkm_plle[] = {
+ "pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0"
+};
+#define mux_plla_clk32_pllp_clkm_plle_idx NULL
+
+static const char *mux_clkm_pllp_pllc_pllre[] = {
+ "clk_m", "pll_p", "pll_c", "pll_re_out"
+};
+static u32 mux_clkm_pllp_pllc_pllre_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3, [3] = 5,
+};
+
+static const char *mux_clkm_48M_pllp_480M[] = {
+ "clk_m", "pll_u_48M", "pll_p", "pll_u_480M"
+};
+#define mux_clkm_48M_pllp_480M_idx NULL
+
+static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = {
+ "clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref"
+};
+static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7,
+};
+
+static const char *mux_plld_out0_plld2_out0[] = {
+ "pll_d_out0", "pll_d2_out0",
+};
+#define mux_plld_out0_plld2_out0_idx NULL
+
+static const char *mux_d_audio_clk[] = {
+ "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync",
+ "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
+};
+static u32 mux_d_audio_clk_idx[] = {
+ [0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001,
+ [5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007,
+};
+
+static const char *mux_pllmcp_clkm[] = {
+ "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud",
+};
+
+static const struct clk_div_table pll_re_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 5 },
+ { .val = 5, .div = 6 },
+ { .val = 0, .div = 0 },
+};
+
+static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
+
+static unsigned long osc_freq;
+static unsigned long pll_ref_freq;
+
+static int __init tegra114_osc_clk_init(void __iomem *clk_base)
+{
+ struct clk *clk;
+ u32 val, pll_ref_div;
+
+ val = readl_relaxed(clk_base + OSC_CTRL);
+
+ osc_freq = tegra114_input_freq[val >> OSC_CTRL_OSC_FREQ_SHIFT];
+ if (!osc_freq) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ /* clk_m */
+ clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT,
+ osc_freq);
+ clk_register_clkdev(clk, "clk_m", NULL);
+ clks[clk_m] = clk;
+
+ /* pll_ref */
+ val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3;
+ pll_ref_div = 1 << val;
+ clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m",
+ CLK_SET_RATE_PARENT, 1, pll_ref_div);
+ clk_register_clkdev(clk, "pll_ref", NULL);
+ clks[pll_ref] = clk;
+
+ pll_ref_freq = osc_freq / pll_ref_div;
+
+ return 0;
+}
+
+static void __init tegra114_fixed_clk_init(void __iomem *clk_base)
+{
+ struct clk *clk;
+
+ /* clk_32k */
+ clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT,
+ 32768);
+ clk_register_clkdev(clk, "clk_32k", NULL);
+ clks[clk_32k] = clk;
+
+ /* clk_m_div2 */
+ clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "clk_m_div2", NULL);
+ clks[clk_m_div2] = clk;
+
+ /* clk_m_div4 */
+ clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m",
+ CLK_SET_RATE_PARENT, 1, 4);
+ clk_register_clkdev(clk, "clk_m_div4", NULL);
+ clks[clk_m_div4] = clk;
+
+}
+
+static __init void tegra114_utmi_param_configure(void __iomem *clk_base)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
+ if (osc_freq == utmi_parameters[i].osc_frequency)
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(utmi_parameters)) {
+ pr_err("%s: Unexpected oscillator freq %lu\n", __func__,
+ osc_freq);
+ return;
+ }
+
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL stable and active counts */
+ /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */
+ reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count);
+
+ reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+
+ reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i].
+ active_delay_count);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL delay and oscillator frequency counts */
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1);
+ reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+
+ reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i].
+ enable_delay_count);
+
+ reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i].
+ xtal_freq_count);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1);
+
+ /* Setup HW control of UTMIPLL */
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET;
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL;
+ reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1);
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1);
+
+ udelay(1);
+
+ /* Setup SW override of UTMIPLL assuming USB2.0
+ ports are assigned to USB2 */
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL;
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ udelay(1);
+
+ /* Enable HW control UTMIPLL */
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+}
+
+static void __init _clip_vco_min(struct tegra_clk_pll_params *pll_params)
+{
+ pll_params->vco_min =
+ DIV_ROUND_UP(pll_params->vco_min, pll_ref_freq) * pll_ref_freq;
+}
+
+static int __init _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params,
+ void __iomem *clk_base)
+{
+ u32 val;
+ u32 step_a, step_b;
+
+ switch (pll_ref_freq) {
+ case 12000000:
+ case 13000000:
+ case 26000000:
+ step_a = 0x2B;
+ step_b = 0x0B;
+ break;
+ case 16800000:
+ step_a = 0x1A;
+ step_b = 0x09;
+ break;
+ case 19200000:
+ step_a = 0x12;
+ step_b = 0x08;
+ break;
+ default:
+ pr_err("%s: Unexpected reference rate %lu\n",
+ __func__, pll_ref_freq);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ val = step_a << pll_params->stepa_shift;
+ val |= step_b << pll_params->stepb_shift;
+ writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg);
+
+ return 0;
+}
+
+static void __init _init_iddq(struct tegra_clk_pll_params *pll_params,
+ void __iomem *clk_base)
+{
+ u32 val, val_iddq;
+
+ val = readl_relaxed(clk_base + pll_params->base_reg);
+ val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg);
+
+ if (val & BIT(30))
+ WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx));
+ else {
+ val_iddq |= BIT(pll_params->iddq_bit_idx);
+ writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg);
+ }
+}
+
+static void __init tegra114_pll_init(void __iomem *clk_base,
+ void __iomem *pmc)
+{
+ u32 val;
+ struct clk *clk;
+
+ /* PLLC */
+ _clip_vco_min(&pll_c_params);
+ if (_setup_dynamic_ramp(&pll_c_params, clk_base) >= 0) {
+ _init_iddq(&pll_c_params, clk_base);
+ clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base,
+ pmc, 0, 0, &pll_c_params, TEGRA_PLL_USE_LOCK,
+ pll_c_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_c", NULL);
+ clks[pll_c] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_c_out1", NULL);
+ clks[pll_c_out1] = clk;
+ }
+
+ /* PLLC2 */
+ _clip_vco_min(&pll_c2_params);
+ clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, 0,
+ &pll_c2_params, TEGRA_PLL_USE_LOCK,
+ pll_cx_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_c2", NULL);
+ clks[pll_c2] = clk;
+
+ /* PLLC3 */
+ _clip_vco_min(&pll_c3_params);
+ clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0, 0,
+ &pll_c3_params, TEGRA_PLL_USE_LOCK,
+ pll_cx_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_c3", NULL);
+ clks[pll_c3] = clk;
+
+ /* PLLP */
+ clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc, 0,
+ 408000000, &pll_p_params,
+ TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK,
+ pll_p_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_p", NULL);
+ clks[pll_p] = clk;
+
+ /* PLLP_OUT1 */
+ clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p",
+ clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div",
+ clk_base + PLLP_OUTA, 1, 0,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out1", NULL);
+ clks[pll_p_out1] = clk;
+
+ /* PLLP_OUT2 */
+ clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p",
+ clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 24, 8, 1,
+ &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div",
+ clk_base + PLLP_OUTA, 17, 16,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out2", NULL);
+ clks[pll_p_out2] = clk;
+
+ /* PLLP_OUT3 */
+ clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p",
+ clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div",
+ clk_base + PLLP_OUTB, 1, 0,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out3", NULL);
+ clks[pll_p_out3] = clk;
+
+ /* PLLP_OUT4 */
+ clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p",
+ clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 24, 8, 1,
+ &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div",
+ clk_base + PLLP_OUTB, 17, 16,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out4", NULL);
+ clks[pll_p_out4] = clk;
+
+ /* PLLM */
+ _clip_vco_min(&pll_m_params);
+ clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0,
+ &pll_m_params, TEGRA_PLL_USE_LOCK,
+ pll_m_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_m", NULL);
+ clks[pll_m] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_m_out1", NULL);
+ clks[pll_m_out1] = clk;
+
+ /* PLLM_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m",
+ CLK_SET_RATE_PARENT, 1, 1);
+
+ /* PLLX */
+ _clip_vco_min(&pll_x_params);
+ if (_setup_dynamic_ramp(&pll_x_params, clk_base) >= 0) {
+ _init_iddq(&pll_x_params, clk_base);
+ clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base,
+ pmc, CLK_IGNORE_UNUSED, 0, &pll_x_params,
+ TEGRA_PLL_USE_LOCK, pll_x_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_x", NULL);
+ clks[pll_x] = clk;
+ }
+
+ /* PLLX_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_x_out0", NULL);
+ clks[pll_x_out0] = clk;
+
+ /* PLLU */
+ val = readl(clk_base + pll_u_params.base_reg);
+ val &= ~BIT(24); /* disable PLLU_OVERRIDE */
+ writel(val, clk_base + pll_u_params.base_reg);
+
+ clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc, 0,
+ 0, &pll_u_params, TEGRA_PLLU |
+ TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK, pll_u_freq_table, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u", NULL);
+ clks[pll_u] = clk;
+
+ tegra114_utmi_param_configure(clk_base);
+
+ /* PLLU_480M */
+ clk = clk_register_gate(NULL, "pll_u_480M", "pll_u",
+ CLK_SET_RATE_PARENT, clk_base + PLLU_BASE,
+ 22, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_480M", NULL);
+ clks[pll_u_480M] = clk;
+
+ /* PLLU_60M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 8);
+ clk_register_clkdev(clk, "pll_u_60M", NULL);
+ clks[pll_u_60M] = clk;
+
+ /* PLLU_48M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 10);
+ clk_register_clkdev(clk, "pll_u_48M", NULL);
+ clks[pll_u_48M] = clk;
+
+ /* PLLU_12M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 40);
+ clk_register_clkdev(clk, "pll_u_12M", NULL);
+ clks[pll_u_12M] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0,
+ 0, &pll_d_params,
+ TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d_lock);
+ clk_register_clkdev(clk, "pll_d", NULL);
+ clks[pll_d] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d_out0", NULL);
+ clks[pll_d_out0] = clk;
+
+ /* PLLD2 */
+ clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc, 0,
+ 0, &pll_d2_params,
+ TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d2_lock);
+ clk_register_clkdev(clk, "pll_d2", NULL);
+ clks[pll_d2] = clk;
+
+ /* PLLD2_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d2_out0", NULL);
+ clks[pll_d2_out0] = clk;
+
+ /* PLLA */
+ clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc, 0,
+ 0, &pll_a_params, TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_a", NULL);
+ clks[pll_a] = clk;
+
+ /* PLLA_OUT0 */
+ clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
+ clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
+ clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_a_out0", NULL);
+ clks[pll_a_out0] = clk;
+
+ /* PLLRE */
+ _clip_vco_min(&pll_re_vco_params);
+ clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc,
+ 0, 0, &pll_re_vco_params, TEGRA_PLL_USE_LOCK,
+ NULL, &pll_re_lock, pll_ref_freq);
+ clk_register_clkdev(clk, "pll_re_vco", NULL);
+ clks[pll_re_vco] = clk;
+
+ clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0,
+ clk_base + PLLRE_BASE, 16, 4, 0,
+ pll_re_div_table, &pll_re_lock);
+ clk_register_clkdev(clk, "pll_re_out", NULL);
+ clks[pll_re_out] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle_tegra114("pll_e_out0", "pll_re_vco",
+ clk_base, 0, 100000000, &pll_e_params,
+ pll_e_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_e_out0", NULL);
+ clks[pll_e_out0] = clk;
+}
+
+static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync",
+ "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
+};
+
+static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2",
+ "clk_m_div4", "extern1",
+};
+
+static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2",
+ "clk_m_div4", "extern2",
+};
+
+static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2",
+ "clk_m_div4", "extern3",
+};
+
+static void __init tegra114_audio_clk_init(void __iomem *clk_base)
+{
+ struct clk *clk;
+
+ /* spdif_in_sync */
+ clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000,
+ 24000000);
+ clk_register_clkdev(clk, "spdif_in_sync", NULL);
+ clks[spdif_in_sync] = clk;
+
+ /* i2s0_sync */
+ clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s0_sync", NULL);
+ clks[i2s0_sync] = clk;
+
+ /* i2s1_sync */
+ clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s1_sync", NULL);
+ clks[i2s1_sync] = clk;
+
+ /* i2s2_sync */
+ clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s2_sync", NULL);
+ clks[i2s2_sync] = clk;
+
+ /* i2s3_sync */
+ clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s3_sync", NULL);
+ clks[i2s3_sync] = clk;
+
+ /* i2s4_sync */
+ clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s4_sync", NULL);
+ clks[i2s4_sync] = clk;
+
+ /* vimclk_sync */
+ clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "vimclk_sync", NULL);
+ clks[vimclk_sync] = clk;
+
+ /* audio0 */
+ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0,
+ NULL);
+ clks[audio0_mux] = clk;
+ clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S0, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio0", NULL);
+ clks[audio0] = clk;
+
+ /* audio1 */
+ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0,
+ NULL);
+ clks[audio1_mux] = clk;
+ clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S1, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio1", NULL);
+ clks[audio1] = clk;
+
+ /* audio2 */
+ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0,
+ NULL);
+ clks[audio2_mux] = clk;
+ clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S2, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio2", NULL);
+ clks[audio2] = clk;
+
+ /* audio3 */
+ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0,
+ NULL);
+ clks[audio3_mux] = clk;
+ clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S3, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio3", NULL);
+ clks[audio3] = clk;
+
+ /* audio4 */
+ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0,
+ NULL);
+ clks[audio4_mux] = clk;
+ clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S4, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio4", NULL);
+ clks[audio4] = clk;
+
+ /* spdif */
+ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0,
+ NULL);
+ clks[spdif_mux] = clk;
+ clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_SPDIF, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "spdif", NULL);
+ clks[spdif] = clk;
+
+ /* audio0_2x */
+ clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio0_div", "audio0_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1,
+ 0, &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 113, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio0_2x", NULL);
+ clks[audio0_2x] = clk;
+
+ /* audio1_2x */
+ clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio1_div", "audio1_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1,
+ 0, &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 114, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio1_2x", NULL);
+ clks[audio1_2x] = clk;
+
+ /* audio2_2x */
+ clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio2_div", "audio2_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1,
+ 0, &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 115, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio2_2x", NULL);
+ clks[audio2_2x] = clk;
+
+ /* audio3_2x */
+ clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio3_div", "audio3_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1,
+ 0, &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 116, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio3_2x", NULL);
+ clks[audio3_2x] = clk;
+
+ /* audio4_2x */
+ clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio4_div", "audio4_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1,
+ 0, &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 117, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio4_2x", NULL);
+ clks[audio4_2x] = clk;
+
+ /* spdif_2x */
+ clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("spdif_div", "spdif_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1,
+ 0, &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 118,
+ &periph_v_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "spdif_2x", NULL);
+ clks[spdif_2x] = clk;
+}
+
+static void __init tegra114_pmc_clk_init(void __iomem *pmc_base)
+{
+ struct clk *clk;
+
+ /* clk_out_1 */
+ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents,
+ ARRAY_SIZE(clk_out1_parents), 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0,
+ &clk_out_lock);
+ clks[clk_out_1_mux] = clk;
+ clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 2, 0,
+ &clk_out_lock);
+ clk_register_clkdev(clk, "extern1", "clk_out_1");
+ clks[clk_out_1] = clk;
+
+ /* clk_out_2 */
+ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents,
+ ARRAY_SIZE(clk_out1_parents), 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0,
+ &clk_out_lock);
+ clks[clk_out_2_mux] = clk;
+ clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 10, 0,
+ &clk_out_lock);
+ clk_register_clkdev(clk, "extern2", "clk_out_2");
+ clks[clk_out_2] = clk;
+
+ /* clk_out_3 */
+ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents,
+ ARRAY_SIZE(clk_out1_parents), 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0,
+ &clk_out_lock);
+ clks[clk_out_3_mux] = clk;
+ clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 18, 0,
+ &clk_out_lock);
+ clk_register_clkdev(clk, "extern3", "clk_out_3");
+ clks[clk_out_3] = clk;
+
+ /* blink */
+ clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0,
+ pmc_base + PMC_DPD_PADS_ORIDE,
+ PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL);
+ clk = clk_register_gate(NULL, "blink", "blink_override", 0,
+ pmc_base + PMC_CTRL,
+ PMC_CTRL_BLINK_ENB, 0, NULL);
+ clk_register_clkdev(clk, "blink", NULL);
+ clks[blink] = clk;
+
+}
+
+static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
+ "pll_p_out3", "pll_p_out2", "unused",
+ "clk_32k", "pll_m_out1" };
+
+static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p", "pll_p_out4", "unused",
+ "unused", "pll_x" };
+
+static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p", "pll_p_out4", "unused",
+ "unused", "pll_x", "pll_x_out0" };
+
+static void __init tegra114_super_clk_init(void __iomem *clk_base)
+{
+ struct clk *clk;
+
+ /* CCLKG */
+ clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents,
+ ARRAY_SIZE(cclk_g_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKG_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ clk_register_clkdev(clk, "cclk_g", NULL);
+ clks[cclk_g] = clk;
+
+ /* CCLKLP */
+ clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents,
+ ARRAY_SIZE(cclk_lp_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKLP_BURST_POLICY,
+ 0, 4, 8, 9, NULL);
+ clk_register_clkdev(clk, "cclk_lp", NULL);
+ clks[cclk_lp] = clk;
+
+ /* SCLK */
+ clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+ ARRAY_SIZE(sclk_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + SCLK_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ clk_register_clkdev(clk, "sclk", NULL);
+ clks[sclk] = clk;
+
+ /* HCLK */
+ clk = clk_register_divider(NULL, "hclk_div", "sclk", 0,
+ clk_base + SYSTEM_CLK_RATE, 4, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT |
+ CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE,
+ 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ clk_register_clkdev(clk, "hclk", NULL);
+ clks[hclk] = clk;
+
+ /* PCLK */
+ clk = clk_register_divider(NULL, "pclk_div", "hclk", 0,
+ clk_base + SYSTEM_CLK_RATE, 0, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT |
+ CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE,
+ 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ clk_register_clkdev(clk, "pclk", NULL);
+ clks[pclk] = clk;
+}
+
+static struct tegra_periph_init_data tegra_periph_clk_list[] = {
+ TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0),
+ TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1),
+ TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2),
+ TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3),
+ TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4),
+ TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out),
+ TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in),
+ TEGRA_INIT_DATA_MUX("pwm", NULL, "pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, &periph_l_regs, TEGRA_PERIPH_ON_APB, pwm),
+ TEGRA_INIT_DATA_MUX("adx", NULL, "adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, &periph_w_regs, TEGRA_PERIPH_ON_APB, adx),
+ TEGRA_INIT_DATA_MUX("amx", NULL, "amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, &periph_w_regs, TEGRA_PERIPH_ON_APB, amx),
+ TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda),
+ TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda2codec_2x),
+ TEGRA_INIT_DATA_MUX("sbc1", NULL, "tegra11-spi.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1),
+ TEGRA_INIT_DATA_MUX("sbc2", NULL, "tegra11-spi.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2),
+ TEGRA_INIT_DATA_MUX("sbc3", NULL, "tegra11-spi.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3),
+ TEGRA_INIT_DATA_MUX("sbc4", NULL, "tegra11-spi.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4),
+ TEGRA_INIT_DATA_MUX("sbc5", NULL, "tegra11-spi.4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5),
+ TEGRA_INIT_DATA_MUX("sbc6", NULL, "tegra11-spi.5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6),
+ TEGRA_INIT_DATA_MUX8("ndflash", NULL, "tegra_nand", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed),
+ TEGRA_INIT_DATA_MUX8("ndspeed", NULL, "tegra_nand_speed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed),
+ TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir),
+ TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1),
+ TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2),
+ TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3),
+ TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4),
+ TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde),
+ TEGRA_INIT_DATA_MUX_FLAGS("csite", NULL, "csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite, CLK_IGNORE_UNUSED),
+ TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la),
+ TEGRA_INIT_DATA_MUX("trace", NULL, "trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, &periph_u_regs, TEGRA_PERIPH_ON_APB, trace),
+ TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr),
+ TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor),
+ TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi),
+ TEGRA_INIT_DATA_I2C("i2c1", "div-clk", "tegra11-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, i2c1),
+ TEGRA_INIT_DATA_I2C("i2c2", "div-clk", "tegra11-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, i2c2),
+ TEGRA_INIT_DATA_I2C("i2c3", "div-clk", "tegra11-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, i2c3),
+ TEGRA_INIT_DATA_I2C("i2c4", "div-clk", "tegra11-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, i2c4),
+ TEGRA_INIT_DATA_I2C("i2c5", "div-clk", "tegra11-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, i2c5),
+ TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta),
+ TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb),
+ TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc),
+ TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd),
+ TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, &periph_l_regs, 0, gr_3d),
+ TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr_2d),
+ TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor),
+ TEGRA_INIT_DATA_INT8("vi", "vi", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi),
+ TEGRA_INIT_DATA_INT8("epp", NULL, "epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp),
+ TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_h_regs, TEGRA_PERIPH_WAR_1005168, msenc),
+ TEGRA_INIT_DATA_INT8("tsec", NULL, "tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, &periph_u_regs, 0, tsec),
+ TEGRA_INIT_DATA_INT8("host1x", NULL, "host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x),
+ TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi),
+ TEGRA_INIT_DATA_MUX("cilab", "cilab", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, &periph_w_regs, 0, cilab),
+ TEGRA_INIT_DATA_MUX("cilcd", "cilcd", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, &periph_w_regs, 0, cilcd),
+ TEGRA_INIT_DATA_MUX("cile", "cile", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, &periph_w_regs, 0, cile),
+ TEGRA_INIT_DATA_MUX("dsialp", "dsialp", "tegradc.0", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, &periph_w_regs, 0, dsialp),
+ TEGRA_INIT_DATA_MUX("dsiblp", "dsiblp", "tegradc.1", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, &periph_w_regs, 0, dsiblp),
+ TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor),
+ TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon),
+ TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1),
+ TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2),
+ TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3),
+ TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow),
+ TEGRA_INIT_DATA_INT8("se", NULL, "se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, TEGRA_PERIPH_ON_APB, se),
+ TEGRA_INIT_DATA_INT_FLAGS("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect, CLK_IGNORE_UNUSED),
+ TEGRA_INIT_DATA_MUX8("soc_therm", NULL, "soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, &periph_u_regs, TEGRA_PERIPH_ON_APB, soc_therm),
+ TEGRA_INIT_DATA_XUSB("xusb_host_src", "host_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, &periph_w_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_host_src),
+ TEGRA_INIT_DATA_XUSB("xusb_falcon_src", "falcon_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_falcon_src),
+ TEGRA_INIT_DATA_XUSB("xusb_fs_src", "fs_src", "tegra_xhci", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_fs_src),
+ TEGRA_INIT_DATA_XUSB("xusb_ss_src", "ss_src", "tegra_xhci", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_ss_src),
+ TEGRA_INIT_DATA_XUSB("xusb_dev_src", "dev_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, &periph_u_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_dev_src),
+ TEGRA_INIT_DATA_AUDIO("d_audio", "d_audio", "tegra30-ahub", CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, TEGRA_PERIPH_ON_APB, d_audio),
+ TEGRA_INIT_DATA_AUDIO("dam0", NULL, "tegra30-dam.0", CLK_SOURCE_DAM0, 108, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam0),
+ TEGRA_INIT_DATA_AUDIO("dam1", NULL, "tegra30-dam.1", CLK_SOURCE_DAM1, 109, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam1),
+ TEGRA_INIT_DATA_AUDIO("dam2", NULL, "tegra30-dam.2", CLK_SOURCE_DAM2, 110, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam2),
+};
+
+static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
+ TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, &periph_l_regs, 0, disp1),
+ TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, &periph_l_regs, 0, disp2),
+};
+
+static __init void tegra114_periph_clk_init(void __iomem *clk_base)
+{
+ struct tegra_periph_init_data *data;
+ struct clk *clk;
+ int i;
+ u32 val;
+
+ /* apbdma */
+ clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base,
+ 0, 34, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[apbdma] = clk;
+
+ /* rtc */
+ clk = tegra_clk_register_periph_gate("rtc", "clk_32k",
+ TEGRA_PERIPH_ON_APB |
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ 0, 4, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "rtc-tegra");
+ clks[rtc] = clk;
+
+ /* kbc */
+ clk = tegra_clk_register_periph_gate("kbc", "clk_32k",
+ TEGRA_PERIPH_ON_APB |
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ 0, 36, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[kbc] = clk;
+
+ /* timer */
+ clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base,
+ 0, 5, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "timer");
+ clks[timer] = clk;
+
+ /* kfuse */
+ clk = tegra_clk_register_periph_gate("kfuse", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 40,
+ &periph_h_regs, periph_clk_enb_refcnt);
+ clks[kfuse] = clk;
+
+ /* fuse */
+ clk = tegra_clk_register_periph_gate("fuse", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 39,
+ &periph_h_regs, periph_clk_enb_refcnt);
+ clks[fuse] = clk;
+
+ /* fuse_burn */
+ clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 39,
+ &periph_h_regs, periph_clk_enb_refcnt);
+ clks[fuse_burn] = clk;
+
+ /* apbif */
+ clk = tegra_clk_register_periph_gate("apbif", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 107,
+ &periph_v_regs, periph_clk_enb_refcnt);
+ clks[apbif] = clk;
+
+ /* hda2hdmi */
+ clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 128,
+ &periph_w_regs, periph_clk_enb_refcnt);
+ clks[hda2hdmi] = clk;
+
+ /* vcp */
+ clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base, 0,
+ 29, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clks[vcp] = clk;
+
+ /* bsea */
+ clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base,
+ 0, 62, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[bsea] = clk;
+
+ /* bsev */
+ clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base,
+ 0, 63, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[bsev] = clk;
+
+ /* mipi-cal */
+ clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base,
+ 0, 56, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[mipi_cal] = clk;
+
+ /* usbd */
+ clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base,
+ 0, 22, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clks[usbd] = clk;
+
+ /* usb2 */
+ clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base,
+ 0, 58, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[usb2] = clk;
+
+ /* usb3 */
+ clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base,
+ 0, 59, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[usb3] = clk;
+
+ /* csi */
+ clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base,
+ 0, 52, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[csi] = clk;
+
+ /* isp */
+ clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0,
+ 23, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clks[isp] = clk;
+
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "clk_m",
+ TEGRA_PERIPH_NO_RESET, clk_base, 0, 92,
+ &periph_u_regs, periph_clk_enb_refcnt);
+ clks[csus] = clk;
+
+ /* dds */
+ clk = tegra_clk_register_periph_gate("dds", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 150,
+ &periph_w_regs, periph_clk_enb_refcnt);
+ clks[dds] = clk;
+
+ /* dp2 */
+ clk = tegra_clk_register_periph_gate("dp2", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 152,
+ &periph_w_regs, periph_clk_enb_refcnt);
+ clks[dp2] = clk;
+
+ /* dtv */
+ clk = tegra_clk_register_periph_gate("dtv", "clk_m",
+ TEGRA_PERIPH_ON_APB, clk_base, 0, 79,
+ &periph_u_regs, periph_clk_enb_refcnt);
+ clks[dtv] = clk;
+
+ /* dsia */
+ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0,
+ ARRAY_SIZE(mux_plld_out0_plld2_out0), 0,
+ clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock);
+ clks[dsia_mux] = clk;
+ clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base,
+ 0, 48, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[dsia] = clk;
+
+ /* dsib */
+ clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0,
+ ARRAY_SIZE(mux_plld_out0_plld2_out0), 0,
+ clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock);
+ clks[dsib_mux] = clk;
+ clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base,
+ 0, 82, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clks[dsib] = clk;
+
+ /* xusb_hs_src */
+ val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC);
+ val |= BIT(25); /* always select PLLU_60M */
+ writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC);
+
+ clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0,
+ 1, 1);
+ clks[xusb_hs_src] = clk;
+
+ /* xusb_host */
+ clk = tegra_clk_register_periph_gate("xusb_host", "xusb_host_src", 0,
+ clk_base, 0, 89, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clks[xusb_host] = clk;
+
+ /* xusb_ss */
+ clk = tegra_clk_register_periph_gate("xusb_ss", "xusb_ss_src", 0,
+ clk_base, 0, 156, &periph_w_regs,
+ periph_clk_enb_refcnt);
+ clks[xusb_host] = clk;
+
+ /* xusb_dev */
+ clk = tegra_clk_register_periph_gate("xusb_dev", "xusb_dev_src", 0,
+ clk_base, 0, 95, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clks[xusb_dev] = clk;
+
+ /* emc */
+ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+ ARRAY_SIZE(mux_pllmcp_clkm), 0,
+ clk_base + CLK_SOURCE_EMC,
+ 29, 3, 0, NULL);
+ clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base,
+ CLK_IGNORE_UNUSED, 57, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clks[emc] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
+ data = &tegra_periph_clk_list[i];
+ clk = tegra_clk_register_periph(data->name, data->parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset, data->flags);
+ clks[data->clk_id] = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) {
+ data = &tegra_periph_nodiv_clk_list[i];
+ clk = tegra_clk_register_periph_nodiv(data->name,
+ data->parent_names, data->num_parents,
+ &data->periph, clk_base, data->offset);
+ clks[data->clk_id] = clk;
+ }
+}
+
+static struct tegra_cpu_car_ops tegra114_cpu_car_ops;
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra114-pmc" },
+ {},
+};
+
+static __initdata struct tegra_clk_init_table init_table[] = {
+ {uarta, pll_p, 408000000, 0},
+ {uartb, pll_p, 408000000, 0},
+ {uartc, pll_p, 408000000, 0},
+ {uartd, pll_p, 408000000, 0},
+ {pll_a, clk_max, 564480000, 1},
+ {pll_a_out0, clk_max, 11289600, 1},
+ {extern1, pll_a_out0, 0, 1},
+ {clk_out_1_mux, extern1, 0, 1},
+ {clk_out_1, clk_max, 0, 1},
+ {i2s0, pll_a_out0, 11289600, 0},
+ {i2s1, pll_a_out0, 11289600, 0},
+ {i2s2, pll_a_out0, 11289600, 0},
+ {i2s3, pll_a_out0, 11289600, 0},
+ {i2s4, pll_a_out0, 11289600, 0},
+ {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */
+};
+
+static void __init tegra114_clock_apply_init_table(void)
+{
+ tegra_init_from_table(init_table, clks, clk_max);
+}
+
+void __init tegra114_clock_init(struct device_node *np)
+{
+ struct device_node *node;
+ int i;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("ioremap tegra114 CAR failed\n");
+ return;
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ WARN_ON(1);
+ return;
+ }
+
+ pmc_base = of_iomap(node, 0);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ WARN_ON(1);
+ return;
+ }
+
+ if (tegra114_osc_clk_init(clk_base) < 0)
+ return;
+
+ tegra114_fixed_clk_init(clk_base);
+ tegra114_pll_init(clk_base, pmc_base);
+ tegra114_periph_clk_init(clk_base);
+ tegra114_audio_clk_init(clk_base);
+ tegra114_pmc_clk_init(pmc_base);
+ tegra114_super_clk_init(clk_base);
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+ if (IS_ERR(clks[i])) {
+ pr_err
+ ("Tegra114 clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ }
+ if (!clks[i])
+ clks[i] = ERR_PTR(-EINVAL);
+ }
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ tegra_clk_apply_init_table = tegra114_clock_apply_init_table;
+
+ tegra_cpu_car_ops = &tegra114_cpu_car_ops;
+}
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
new file mode 100644
index 000000000..075db0c99
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -0,0 +1,1341 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk/tegra.h>
+#include <linux/delay.h>
+
+#include "clk.h"
+
+#define RST_DEVICES_L 0x004
+#define RST_DEVICES_H 0x008
+#define RST_DEVICES_U 0x00c
+#define RST_DEVICES_SET_L 0x300
+#define RST_DEVICES_CLR_L 0x304
+#define RST_DEVICES_SET_H 0x308
+#define RST_DEVICES_CLR_H 0x30c
+#define RST_DEVICES_SET_U 0x310
+#define RST_DEVICES_CLR_U 0x314
+#define RST_DEVICES_NUM 3
+
+#define CLK_OUT_ENB_L 0x010
+#define CLK_OUT_ENB_H 0x014
+#define CLK_OUT_ENB_U 0x018
+#define CLK_OUT_ENB_SET_L 0x320
+#define CLK_OUT_ENB_CLR_L 0x324
+#define CLK_OUT_ENB_SET_H 0x328
+#define CLK_OUT_ENB_CLR_H 0x32c
+#define CLK_OUT_ENB_SET_U 0x330
+#define CLK_OUT_ENB_CLR_U 0x334
+#define CLK_OUT_ENB_NUM 3
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_MASK (3<<30)
+#define OSC_CTRL_OSC_FREQ_13MHZ (0<<30)
+#define OSC_CTRL_OSC_FREQ_19_2MHZ (1<<30)
+#define OSC_CTRL_OSC_FREQ_12MHZ (2<<30)
+#define OSC_CTRL_OSC_FREQ_26MHZ (3<<30)
+#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
+
+#define OSC_CTRL_PLL_REF_DIV_MASK (3<<28)
+#define OSC_CTRL_PLL_REF_DIV_1 (0<<28)
+#define OSC_CTRL_PLL_REF_DIV_2 (1<<28)
+#define OSC_CTRL_PLL_REF_DIV_4 (2<<28)
+
+#define OSC_FREQ_DET 0x58
+#define OSC_FREQ_DET_TRIG (1<<31)
+
+#define OSC_FREQ_DET_STATUS 0x5c
+#define OSC_FREQ_DET_BUSY (1<<31)
+#define OSC_FREQ_DET_CNT_MASK 0xFFFF
+
+#define PLLS_BASE 0xf0
+#define PLLS_MISC 0xf4
+#define PLLC_BASE 0x80
+#define PLLC_MISC 0x8c
+#define PLLM_BASE 0x90
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+
+#define PLLC_OUT 0x84
+#define PLLM_OUT 0x94
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
+#define PLLA_OUT 0xb4
+
+#define CCLK_BURST_POLICY 0x20
+#define SUPER_CCLK_DIVIDER 0x24
+#define SCLK_BURST_POLICY 0x28
+#define SUPER_SCLK_DIVIDER 0x2c
+#define CLK_SYSTEM_RATE 0x30
+
+#define CCLK_BURST_POLICY_SHIFT 28
+#define CCLK_RUN_POLICY_SHIFT 4
+#define CCLK_IDLE_POLICY_SHIFT 0
+#define CCLK_IDLE_POLICY 1
+#define CCLK_RUN_POLICY 2
+#define CCLK_BURST_POLICY_PLLX 8
+
+#define CLK_SOURCE_I2S1 0x100
+#define CLK_SOURCE_I2S2 0x104
+#define CLK_SOURCE_SPDIF_OUT 0x108
+#define CLK_SOURCE_SPDIF_IN 0x10c
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_SPI 0x114
+#define CLK_SOURCE_SBC1 0x134
+#define CLK_SOURCE_SBC2 0x118
+#define CLK_SOURCE_SBC3 0x11c
+#define CLK_SOURCE_SBC4 0x1b4
+#define CLK_SOURCE_XIO 0x120
+#define CLK_SOURCE_TWC 0x12c
+#define CLK_SOURCE_IDE 0x144
+#define CLK_SOURCE_NDFLASH 0x160
+#define CLK_SOURCE_VFIR 0x168
+#define CLK_SOURCE_SDMMC1 0x150
+#define CLK_SOURCE_SDMMC2 0x154
+#define CLK_SOURCE_SDMMC3 0x1bc
+#define CLK_SOURCE_SDMMC4 0x164
+#define CLK_SOURCE_CVE 0x140
+#define CLK_SOURCE_TVO 0x188
+#define CLK_SOURCE_TVDAC 0x194
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_DISP1 0x138
+#define CLK_SOURCE_DISP2 0x13c
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_LA 0x1f8
+#define CLK_SOURCE_OWR 0x1cc
+#define CLK_SOURCE_NOR 0x1d0
+#define CLK_SOURCE_MIPI 0x174
+#define CLK_SOURCE_I2C1 0x124
+#define CLK_SOURCE_I2C2 0x198
+#define CLK_SOURCE_I2C3 0x1b8
+#define CLK_SOURCE_DVC 0x128
+#define CLK_SOURCE_UARTA 0x178
+#define CLK_SOURCE_UARTB 0x17c
+#define CLK_SOURCE_UARTC 0x1a0
+#define CLK_SOURCE_UARTD 0x1c0
+#define CLK_SOURCE_UARTE 0x1c4
+#define CLK_SOURCE_3D 0x158
+#define CLK_SOURCE_2D 0x15c
+#define CLK_SOURCE_MPE 0x170
+#define CLK_SOURCE_EPP 0x16c
+#define CLK_SOURCE_HOST1X 0x180
+#define CLK_SOURCE_VDE 0x1c8
+#define CLK_SOURCE_VI 0x148
+#define CLK_SOURCE_VI_SENSOR 0x1a8
+#define CLK_SOURCE_EMC 0x19c
+
+#define AUDIO_SYNC_CLK 0x38
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_BLINK_ENB 7
+#define PMC_DPD_PADS_ORIDE 0x1c
+#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20
+#define PMC_BLINK_TIMER 0x40
+
+/* Tegra CPU clock and reset control regs */
+#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344
+
+#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
+#define CPU_RESET(cpu) (0x1111ul << (cpu))
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 pllx_misc;
+ u32 pllx_base;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+} tegra20_cpu_clk_sctx;
+#endif
+
+static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32];
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+
+static DEFINE_SPINLOCK(pll_div_lock);
+static DEFINE_SPINLOCK(sysrate_lock);
+
+#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \
+ _regs, _clk_num, periph_clk_enb_refcnt, \
+ _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id)
+
+#define TEGRA_INIT_DATA_DIV16(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id)
+
+#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \
+ _mux_shift, _mux_width, _clk_num, _regs, \
+ _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ _mux_shift, _mux_width, 0, 0, 0, 0, 0, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id)
+
+/* IDs assigned here must be in sync with DT bindings definition
+ * for Tegra20 clocks .
+ */
+enum tegra20_clk {
+ cpu, ac97 = 3, rtc, timer, uarta, gpio = 8, sdmmc2, i2s1 = 11, i2c1,
+ ndflash, sdmmc1, sdmmc4, twc, pwm, i2s2, epp, gr2d = 21, usbd, isp,
+ gr3d, ide, disp2, disp1, host1x, vcp, cache2 = 31, mem, ahbdma, apbdma,
+ kbc = 36, stat_mon, pmc, fuse, kfuse, sbc1, nor, spi, sbc2, xio, sbc3,
+ dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2,
+ usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3,
+ pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb,
+ iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev2, cdev1,
+ uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve,
+ osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0,
+ pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1,
+ pll_p, pll_p_out1, pll_p_out2, pll_p_out3, pll_p_out4, pll_s, pll_u,
+ pll_x, cop, audio, pll_ref, twd, clk_max,
+};
+
+static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 600000000, 600, 12, 0, 8 },
+ { 13000000, 600000000, 600, 13, 0, 8 },
+ { 19200000, 600000000, 500, 16, 0, 6 },
+ { 26000000, 600000000, 600, 26, 0, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 666000000, 666, 12, 0, 8},
+ { 13000000, 666000000, 666, 13, 0, 8},
+ { 19200000, 666000000, 555, 16, 0, 8},
+ { 26000000, 666000000, 666, 26, 0, 8},
+ { 12000000, 600000000, 600, 12, 0, 8},
+ { 13000000, 600000000, 600, 13, 0, 8},
+ { 19200000, 600000000, 375, 12, 0, 6},
+ { 26000000, 600000000, 600, 26, 0, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 1, 8},
+ { 13000000, 216000000, 432, 13, 1, 8},
+ { 19200000, 216000000, 90, 4, 1, 1},
+ { 26000000, 216000000, 432, 26, 1, 8},
+ { 12000000, 432000000, 432, 12, 0, 8},
+ { 13000000, 432000000, 432, 13, 0, 8},
+ { 19200000, 432000000, 90, 4, 0, 1},
+ { 26000000, 432000000, 432, 26, 0, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 28800000, 56448000, 49, 25, 0, 1},
+ { 28800000, 73728000, 64, 25, 0, 1},
+ { 28800000, 24000000, 5, 6, 0, 1},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 216000000, 216, 12, 0, 4},
+ { 13000000, 216000000, 216, 13, 0, 4},
+ { 19200000, 216000000, 135, 12, 0, 3},
+ { 26000000, 216000000, 216, 26, 0, 4},
+
+ { 12000000, 594000000, 594, 12, 0, 8},
+ { 13000000, 594000000, 594, 13, 0, 8},
+ { 19200000, 594000000, 495, 16, 0, 8},
+ { 26000000, 594000000, 594, 26, 0, 8},
+
+ { 12000000, 1000000000, 1000, 12, 0, 12},
+ { 13000000, 1000000000, 1000, 13, 0, 12},
+ { 19200000, 1000000000, 625, 12, 0, 8},
+ { 26000000, 1000000000, 1000, 26, 0, 12},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 0, 0},
+ { 13000000, 480000000, 960, 13, 0, 0},
+ { 19200000, 480000000, 200, 4, 0, 0},
+ { 26000000, 480000000, 960, 26, 0, 0},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1 GHz */
+ { 12000000, 1000000000, 1000, 12, 0, 12},
+ { 13000000, 1000000000, 1000, 13, 0, 12},
+ { 19200000, 1000000000, 625, 12, 0, 8},
+ { 26000000, 1000000000, 1000, 26, 0, 12},
+
+ /* 912 MHz */
+ { 12000000, 912000000, 912, 12, 0, 12},
+ { 13000000, 912000000, 912, 13, 0, 12},
+ { 19200000, 912000000, 760, 16, 0, 8},
+ { 26000000, 912000000, 912, 26, 0, 12},
+
+ /* 816 MHz */
+ { 12000000, 816000000, 816, 12, 0, 12},
+ { 13000000, 816000000, 816, 13, 0, 12},
+ { 19200000, 816000000, 680, 16, 0, 8},
+ { 26000000, 816000000, 816, 26, 0, 12},
+
+ /* 760 MHz */
+ { 12000000, 760000000, 760, 12, 0, 12},
+ { 13000000, 760000000, 760, 13, 0, 12},
+ { 19200000, 760000000, 950, 24, 0, 8},
+ { 26000000, 760000000, 760, 26, 0, 12},
+
+ /* 750 MHz */
+ { 12000000, 750000000, 750, 12, 0, 12},
+ { 13000000, 750000000, 750, 13, 0, 12},
+ { 19200000, 750000000, 625, 16, 0, 8},
+ { 26000000, 750000000, 750, 26, 0, 12},
+
+ /* 608 MHz */
+ { 12000000, 608000000, 608, 12, 0, 12},
+ { 13000000, 608000000, 608, 13, 0, 12},
+ { 19200000, 608000000, 380, 12, 0, 8},
+ { 26000000, 608000000, 608, 26, 0, 12},
+
+ /* 456 MHz */
+ { 12000000, 456000000, 456, 12, 0, 12},
+ { 13000000, 456000000, 456, 13, 0, 12},
+ { 19200000, 456000000, 380, 16, 0, 8},
+ { 26000000, 456000000, 456, 26, 0, 12},
+
+ /* 312 MHz */
+ { 12000000, 312000000, 312, 12, 0, 12},
+ { 13000000, 312000000, 312, 13, 0, 12},
+ { 19200000, 312000000, 260, 16, 0, 8},
+ { 26000000, 312000000, 312, 26, 0, 12},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ { 12000000, 100000000, 200, 24, 0, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+/* PLL parameters */
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+};
+
+static struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_params pll_u_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 48000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 12000000,
+ .cf_min = 0,
+ .cf_max = 0,
+ .vco_min = 0,
+ .vco_max = 0,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 0,
+};
+
+/* Peripheral clock registers */
+static struct tegra_clk_periph_regs periph_l_regs = {
+ .enb_reg = CLK_OUT_ENB_L,
+ .enb_set_reg = CLK_OUT_ENB_SET_L,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_L,
+ .rst_reg = RST_DEVICES_L,
+ .rst_set_reg = RST_DEVICES_SET_L,
+ .rst_clr_reg = RST_DEVICES_CLR_L,
+};
+
+static struct tegra_clk_periph_regs periph_h_regs = {
+ .enb_reg = CLK_OUT_ENB_H,
+ .enb_set_reg = CLK_OUT_ENB_SET_H,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_H,
+ .rst_reg = RST_DEVICES_H,
+ .rst_set_reg = RST_DEVICES_SET_H,
+ .rst_clr_reg = RST_DEVICES_CLR_H,
+};
+
+static struct tegra_clk_periph_regs periph_u_regs = {
+ .enb_reg = CLK_OUT_ENB_U,
+ .enb_set_reg = CLK_OUT_ENB_SET_U,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_U,
+ .rst_reg = RST_DEVICES_U,
+ .rst_set_reg = RST_DEVICES_SET_U,
+ .rst_clr_reg = RST_DEVICES_CLR_U,
+};
+
+static unsigned long tegra20_clk_measure_input_freq(void)
+{
+ u32 osc_ctrl = readl_relaxed(clk_base + OSC_CTRL);
+ u32 auto_clk_control = osc_ctrl & OSC_CTRL_OSC_FREQ_MASK;
+ u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
+ unsigned long input_freq;
+
+ switch (auto_clk_control) {
+ case OSC_CTRL_OSC_FREQ_12MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 12000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_13MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 13000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_19_2MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 19200000;
+ break;
+ case OSC_CTRL_OSC_FREQ_26MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 26000000;
+ break;
+ default:
+ pr_err("Unexpected clock autodetect value %d",
+ auto_clk_control);
+ BUG();
+ return 0;
+ }
+
+ return input_freq;
+}
+
+static unsigned int tegra20_get_pll_ref_div(void)
+{
+ u32 pll_ref_div = readl_relaxed(clk_base + OSC_CTRL) &
+ OSC_CTRL_PLL_REF_DIV_MASK;
+
+ switch (pll_ref_div) {
+ case OSC_CTRL_PLL_REF_DIV_1:
+ return 1;
+ case OSC_CTRL_PLL_REF_DIV_2:
+ return 2;
+ case OSC_CTRL_PLL_REF_DIV_4:
+ return 4;
+ default:
+ pr_err("Invalied pll ref divider %d\n", pll_ref_div);
+ BUG();
+ }
+ return 0;
+}
+
+static void tegra20_pll_init(void)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, NULL, 0,
+ 0, &pll_c_params, TEGRA_PLL_HAS_CPCON,
+ pll_c_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_c", NULL);
+ clks[pll_c] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0, CLK_SET_RATE_PARENT,
+ 0, NULL);
+ clk_register_clkdev(clk, "pll_c_out1", NULL);
+ clks[pll_c_out1] = clk;
+
+ /* PLLP */
+ clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, NULL, 0,
+ 216000000, &pll_p_params, TEGRA_PLL_FIXED |
+ TEGRA_PLL_HAS_CPCON, pll_p_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_p", NULL);
+ clks[pll_p] = clk;
+
+ /* PLLP_OUT1 */
+ clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p",
+ clk_base + PLLP_OUTA, 0,
+ TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div",
+ clk_base + PLLP_OUTA, 1, 0,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out1", NULL);
+ clks[pll_p_out1] = clk;
+
+ /* PLLP_OUT2 */
+ clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p",
+ clk_base + PLLP_OUTA, 0,
+ TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP,
+ 24, 8, 1, &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div",
+ clk_base + PLLP_OUTA, 17, 16,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out2", NULL);
+ clks[pll_p_out2] = clk;
+
+ /* PLLP_OUT3 */
+ clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p",
+ clk_base + PLLP_OUTB, 0,
+ TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div",
+ clk_base + PLLP_OUTB, 1, 0,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out3", NULL);
+ clks[pll_p_out3] = clk;
+
+ /* PLLP_OUT4 */
+ clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p",
+ clk_base + PLLP_OUTB, 0,
+ TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP,
+ 24, 8, 1, &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div",
+ clk_base + PLLP_OUTB, 17, 16,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out4", NULL);
+ clks[pll_p_out4] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, NULL,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0,
+ &pll_m_params, TEGRA_PLL_HAS_CPCON,
+ pll_m_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_m", NULL);
+ clks[pll_m] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_m_out1", NULL);
+ clks[pll_m_out1] = clk;
+
+ /* PLLX */
+ clk = tegra_clk_register_pll("pll_x", "pll_ref", clk_base, NULL, 0,
+ 0, &pll_x_params, TEGRA_PLL_HAS_CPCON,
+ pll_x_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_x", NULL);
+ clks[pll_x] = clk;
+
+ /* PLLU */
+ clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, NULL, 0,
+ 0, &pll_u_params, TEGRA_PLLU | TEGRA_PLL_HAS_CPCON,
+ pll_u_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_u", NULL);
+ clks[pll_u] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, NULL, 0,
+ 0, &pll_d_params, TEGRA_PLL_HAS_CPCON,
+ pll_d_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_d", NULL);
+ clks[pll_d] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d_out0", NULL);
+ clks[pll_d_out0] = clk;
+
+ /* PLLA */
+ clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, NULL, 0,
+ 0, &pll_a_params, TEGRA_PLL_HAS_CPCON,
+ pll_a_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_a", NULL);
+ clks[pll_a] = clk;
+
+ /* PLLA_OUT0 */
+ clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
+ clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
+ clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_a_out0", NULL);
+ clks[pll_a_out0] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, pmc_base,
+ 0, 100000000, &pll_e_params,
+ 0, pll_e_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_e", NULL);
+ clks[pll_e] = clk;
+}
+
+static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p", "pll_p_out4",
+ "pll_p_out3", "clk_d", "pll_x" };
+static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
+ "pll_p_out3", "pll_p_out2", "clk_d",
+ "clk_32k", "pll_m_out1" };
+
+static void tegra20_super_clk_init(void)
+{
+ struct clk *clk;
+
+ /* CCLK */
+ clk = tegra_clk_register_super_mux("cclk", cclk_parents,
+ ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT,
+ clk_base + CCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
+ clk_register_clkdev(clk, "cclk", NULL);
+ clks[cclk] = clk;
+
+ /* SCLK */
+ clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+ ARRAY_SIZE(sclk_parents), CLK_SET_RATE_PARENT,
+ clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
+ clk_register_clkdev(clk, "sclk", NULL);
+ clks[sclk] = clk;
+
+ /* HCLK */
+ clk = clk_register_divider(NULL, "hclk_div", "sclk", 0,
+ clk_base + CLK_SYSTEM_RATE, 4, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT,
+ clk_base + CLK_SYSTEM_RATE, 7,
+ CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ clk_register_clkdev(clk, "hclk", NULL);
+ clks[hclk] = clk;
+
+ /* PCLK */
+ clk = clk_register_divider(NULL, "pclk_div", "hclk", 0,
+ clk_base + CLK_SYSTEM_RATE, 0, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT,
+ clk_base + CLK_SYSTEM_RATE, 3,
+ CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ clk_register_clkdev(clk, "pclk", NULL);
+ clks[pclk] = clk;
+
+ /* twd */
+ clk = clk_register_fixed_factor(NULL, "twd", "cclk", 0, 1, 4);
+ clk_register_clkdev(clk, "twd", NULL);
+ clks[twd] = clk;
+}
+
+static const char *audio_parents[] = {"spdif_in", "i2s1", "i2s2", "unused",
+ "pll_a_out0", "unused", "unused",
+ "unused"};
+
+static void __init tegra20_audio_clk_init(void)
+{
+ struct clk *clk;
+
+ /* audio */
+ clk = clk_register_mux(NULL, "audio_mux", audio_parents,
+ ARRAY_SIZE(audio_parents), 0,
+ clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio", "audio_mux", 0,
+ clk_base + AUDIO_SYNC_CLK, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio", NULL);
+ clks[audio] = clk;
+
+ /* audio_2x */
+ clk = clk_register_fixed_factor(NULL, "audio_doubler", "audio",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_periph_gate("audio_2x", "audio_doubler",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 89, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio_2x", NULL);
+ clks[audio_2x] = clk;
+
+}
+
+static const char *i2s1_parents[] = {"pll_a_out0", "audio_2x", "pll_p",
+ "clk_m"};
+static const char *i2s2_parents[] = {"pll_a_out0", "audio_2x", "pll_p",
+ "clk_m"};
+static const char *spdif_out_parents[] = {"pll_a_out0", "audio_2x", "pll_p",
+ "clk_m"};
+static const char *spdif_in_parents[] = {"pll_p", "pll_c", "pll_m"};
+static const char *pwm_parents[] = {"pll_p", "pll_c", "audio", "clk_m",
+ "clk_32k"};
+static const char *mux_pllpcm_clkm[] = {"pll_p", "pll_c", "pll_m", "clk_m"};
+static const char *mux_pllmcpa[] = {"pll_m", "pll_c", "pll_c", "pll_a"};
+static const char *mux_pllpdc_clkm[] = {"pll_p", "pll_d_out0", "pll_c",
+ "clk_m"};
+static const char *mux_pllmcp_clkm[] = {"pll_m", "pll_c", "pll_p", "clk_m"};
+
+static struct tegra_periph_init_data tegra_periph_clk_list[] = {
+ TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra20-i2s.0", i2s1_parents, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1),
+ TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra20-i2s.1", i2s2_parents, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2),
+ TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra20-spdif", spdif_out_parents, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out),
+ TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra20-spdif", spdif_in_parents, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in),
+ TEGRA_INIT_DATA_MUX("sbc1", NULL, "spi_tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1),
+ TEGRA_INIT_DATA_MUX("sbc2", NULL, "spi_tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2),
+ TEGRA_INIT_DATA_MUX("sbc3", NULL, "spi_tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3),
+ TEGRA_INIT_DATA_MUX("sbc4", NULL, "spi_tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4),
+ TEGRA_INIT_DATA_MUX("spi", NULL, "spi", mux_pllpcm_clkm, CLK_SOURCE_SPI, 43, &periph_h_regs, TEGRA_PERIPH_ON_APB, spi),
+ TEGRA_INIT_DATA_MUX("xio", NULL, "xio", mux_pllpcm_clkm, CLK_SOURCE_XIO, 45, &periph_h_regs, 0, xio),
+ TEGRA_INIT_DATA_MUX("twc", NULL, "twc", mux_pllpcm_clkm, CLK_SOURCE_TWC, 16, &periph_l_regs, TEGRA_PERIPH_ON_APB, twc),
+ TEGRA_INIT_DATA_MUX("ide", NULL, "ide", mux_pllpcm_clkm, CLK_SOURCE_XIO, 25, &periph_l_regs, 0, ide),
+ TEGRA_INIT_DATA_MUX("ndflash", NULL, "tegra_nand", mux_pllpcm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_l_regs, 0, ndflash),
+ TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllpcm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir),
+ TEGRA_INIT_DATA_MUX("csite", NULL, "csite", mux_pllpcm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, 0, csite),
+ TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllpcm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, 0, la),
+ TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllpcm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr),
+ TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllpcm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi),
+ TEGRA_INIT_DATA_MUX("vde", NULL, "vde", mux_pllpcm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde),
+ TEGRA_INIT_DATA_MUX("vi", "vi", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi),
+ TEGRA_INIT_DATA_MUX("epp", NULL, "epp", mux_pllmcpa, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp),
+ TEGRA_INIT_DATA_MUX("mpe", NULL, "mpe", mux_pllmcpa, CLK_SOURCE_MPE, 60, &periph_h_regs, 0, mpe),
+ TEGRA_INIT_DATA_MUX("host1x", NULL, "host1x", mux_pllmcpa, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x),
+ TEGRA_INIT_DATA_MUX("3d", NULL, "3d", mux_pllmcpa, CLK_SOURCE_3D, 24, &periph_l_regs, TEGRA_PERIPH_MANUAL_RESET, gr3d),
+ TEGRA_INIT_DATA_MUX("2d", NULL, "2d", mux_pllmcpa, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr2d),
+ TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllpcm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor),
+ TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1),
+ TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2),
+ TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3),
+ TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4),
+ TEGRA_INIT_DATA_MUX("cve", NULL, "cve", mux_pllpdc_clkm, CLK_SOURCE_CVE, 49, &periph_h_regs, 0, cve),
+ TEGRA_INIT_DATA_MUX("tvo", NULL, "tvo", mux_pllpdc_clkm, CLK_SOURCE_TVO, 49, &periph_h_regs, 0, tvo),
+ TEGRA_INIT_DATA_MUX("tvdac", NULL, "tvdac", mux_pllpdc_clkm, CLK_SOURCE_TVDAC, 53, &periph_h_regs, 0, tvdac),
+ TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor),
+ TEGRA_INIT_DATA_DIV16("i2c1", "div-clk", "tegra-i2c.0", mux_pllpcm_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2c1),
+ TEGRA_INIT_DATA_DIV16("i2c2", "div-clk", "tegra-i2c.1", mux_pllpcm_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, TEGRA_PERIPH_ON_APB, i2c2),
+ TEGRA_INIT_DATA_DIV16("i2c3", "div-clk", "tegra-i2c.2", mux_pllpcm_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2c3),
+ TEGRA_INIT_DATA_DIV16("dvc", "div-clk", "tegra-i2c.3", mux_pllpcm_clkm, CLK_SOURCE_DVC, 47, &periph_h_regs, TEGRA_PERIPH_ON_APB, dvc),
+ TEGRA_INIT_DATA_MUX("hdmi", NULL, "hdmi", mux_pllpdc_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi),
+ TEGRA_INIT_DATA("pwm", NULL, "tegra-pwm", pwm_parents, CLK_SOURCE_PWM, 28, 3, 0, 0, 8, 1, 0, &periph_l_regs, 17, periph_clk_enb_refcnt, TEGRA_PERIPH_ON_APB, pwm),
+};
+
+static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
+ TEGRA_INIT_DATA_NODIV("uarta", NULL, "tegra_uart.0", mux_pllpcm_clkm, CLK_SOURCE_UARTA, 30, 2, 6, &periph_l_regs, TEGRA_PERIPH_ON_APB, uarta),
+ TEGRA_INIT_DATA_NODIV("uartb", NULL, "tegra_uart.1", mux_pllpcm_clkm, CLK_SOURCE_UARTB, 30, 2, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, uartb),
+ TEGRA_INIT_DATA_NODIV("uartc", NULL, "tegra_uart.2", mux_pllpcm_clkm, CLK_SOURCE_UARTC, 30, 2, 55, &periph_h_regs, TEGRA_PERIPH_ON_APB, uartc),
+ TEGRA_INIT_DATA_NODIV("uartd", NULL, "tegra_uart.3", mux_pllpcm_clkm, CLK_SOURCE_UARTD, 30, 2, 65, &periph_u_regs, TEGRA_PERIPH_ON_APB, uartd),
+ TEGRA_INIT_DATA_NODIV("uarte", NULL, "tegra_uart.4", mux_pllpcm_clkm, CLK_SOURCE_UARTE, 30, 2, 66, &periph_u_regs, TEGRA_PERIPH_ON_APB, uarte),
+ TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllpdc_clkm, CLK_SOURCE_DISP1, 30, 2, 27, &periph_l_regs, 0, disp1),
+ TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, &periph_l_regs, 0, disp2),
+};
+
+static void __init tegra20_periph_clk_init(void)
+{
+ struct tegra_periph_init_data *data;
+ struct clk *clk;
+ int i;
+
+ /* ac97 */
+ clk = tegra_clk_register_periph_gate("ac97", "pll_a_out0",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 3, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra20-ac97");
+ clks[ac97] = clk;
+
+ /* apbdma */
+ clk = tegra_clk_register_periph_gate("apbdma", "pclk", 0, clk_base,
+ 0, 34, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-apbdma");
+ clks[apbdma] = clk;
+
+ /* rtc */
+ clk = tegra_clk_register_periph_gate("rtc", "clk_32k",
+ TEGRA_PERIPH_NO_RESET,
+ clk_base, 0, 4, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "rtc-tegra");
+ clks[rtc] = clk;
+
+ /* timer */
+ clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base,
+ 0, 5, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "timer");
+ clks[timer] = clk;
+
+ /* kbc */
+ clk = tegra_clk_register_periph_gate("kbc", "clk_32k",
+ TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 36, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-kbc");
+ clks[kbc] = clk;
+
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "clk_m",
+ TEGRA_PERIPH_NO_RESET,
+ clk_base, 0, 92, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "csus", "tengra_camera");
+ clks[csus] = clk;
+
+ /* vcp */
+ clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0,
+ clk_base, 0, 29, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "vcp", "tegra-avp");
+ clks[vcp] = clk;
+
+ /* bsea */
+ clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0,
+ clk_base, 0, 62, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "bsea", "tegra-avp");
+ clks[bsea] = clk;
+
+ /* bsev */
+ clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0,
+ clk_base, 0, 63, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "bsev", "tegra-aes");
+ clks[bsev] = clk;
+
+ /* emc */
+ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+ ARRAY_SIZE(mux_pllmcp_clkm), 0,
+ clk_base + CLK_SOURCE_EMC,
+ 30, 2, 0, NULL);
+ clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
+ 57, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "emc", NULL);
+ clks[emc] = clk;
+
+ /* usbd */
+ clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, 0,
+ 22, &periph_l_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "fsl-tegra-udc");
+ clks[usbd] = clk;
+
+ /* usb2 */
+ clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, 0,
+ 58, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-ehci.1");
+ clks[usb2] = clk;
+
+ /* usb3 */
+ clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, 0,
+ 59, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-ehci.2");
+ clks[usb3] = clk;
+
+ /* dsi */
+ clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
+ 48, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "dsi");
+ clks[dsi] = clk;
+
+ /* csi */
+ clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base,
+ 0, 52, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "csi", "tegra_camera");
+ clks[csi] = clk;
+
+ /* isp */
+ clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, 23,
+ &periph_l_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "isp", "tegra_camera");
+ clks[isp] = clk;
+
+ /* pex */
+ clk = tegra_clk_register_periph_gate("pex", "clk_m", 0, clk_base, 0, 70,
+ &periph_u_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "pex", NULL);
+ clks[pex] = clk;
+
+ /* afi */
+ clk = tegra_clk_register_periph_gate("afi", "clk_m", 0, clk_base, 0, 72,
+ &periph_u_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "afi", NULL);
+ clks[afi] = clk;
+
+ /* pcie_xclk */
+ clk = tegra_clk_register_periph_gate("pcie_xclk", "clk_m", 0, clk_base,
+ 0, 74, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "pcie_xclk", NULL);
+ clks[pcie_xclk] = clk;
+
+ /* cdev1 */
+ clk = clk_register_fixed_rate(NULL, "cdev1_fixed", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk = tegra_clk_register_periph_gate("cdev1", "cdev1_fixed", 0,
+ clk_base, 0, 94, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "cdev1", NULL);
+ clks[cdev1] = clk;
+
+ /* cdev2 */
+ clk = clk_register_fixed_rate(NULL, "cdev2_fixed", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk = tegra_clk_register_periph_gate("cdev2", "cdev2_fixed", 0,
+ clk_base, 0, 93, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "cdev2", NULL);
+ clks[cdev2] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
+ data = &tegra_periph_clk_list[i];
+ clk = tegra_clk_register_periph(data->name, data->parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset, data->flags);
+ clk_register_clkdev(clk, data->con_id, data->dev_id);
+ clks[data->clk_id] = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) {
+ data = &tegra_periph_nodiv_clk_list[i];
+ clk = tegra_clk_register_periph_nodiv(data->name,
+ data->parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset);
+ clk_register_clkdev(clk, data->con_id, data->dev_id);
+ clks[data->clk_id] = clk;
+ }
+}
+
+
+static void __init tegra20_fixed_clk_init(void)
+{
+ struct clk *clk;
+
+ /* clk_32k */
+ clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT,
+ 32768);
+ clk_register_clkdev(clk, "clk_32k", NULL);
+ clks[clk_32k] = clk;
+}
+
+static void __init tegra20_pmc_clk_init(void)
+{
+ struct clk *clk;
+
+ /* blink */
+ writel_relaxed(0, pmc_base + PMC_BLINK_TIMER);
+ clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0,
+ pmc_base + PMC_DPD_PADS_ORIDE,
+ PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL);
+ clk = clk_register_gate(NULL, "blink", "blink_override", 0,
+ pmc_base + PMC_CTRL,
+ PMC_CTRL_BLINK_ENB, 0, NULL);
+ clk_register_clkdev(clk, "blink", NULL);
+ clks[blink] = clk;
+}
+
+static void __init tegra20_osc_clk_init(void)
+{
+ struct clk *clk;
+ unsigned long input_freq;
+ unsigned int pll_ref_div;
+
+ input_freq = tegra20_clk_measure_input_freq();
+
+ /* clk_m */
+ clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT |
+ CLK_IGNORE_UNUSED, input_freq);
+ clk_register_clkdev(clk, "clk_m", NULL);
+ clks[clk_m] = clk;
+
+ /* pll_ref */
+ pll_ref_div = tegra20_get_pll_ref_div();
+ clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m",
+ CLK_SET_RATE_PARENT, 1, pll_ref_div);
+ clk_register_clkdev(clk, "pll_ref", NULL);
+ clks[pll_ref] = clk;
+}
+
+/* Tegra20 CPU clock and reset control functions */
+static void tegra20_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base +
+ TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+
+ return;
+}
+
+static void tegra20_put_cpu_in_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ dmb();
+}
+
+static void tegra20_cpu_out_of_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+ wmb();
+}
+
+static void tegra20_enable_cpu_clock(u32 cpu)
+{
+ unsigned int reg;
+
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg & ~CPU_CLOCK(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ barrier();
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+}
+
+static void tegra20_disable_cpu_clock(u32 cpu)
+{
+ unsigned int reg;
+
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg | CPU_CLOCK(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static bool tegra20_cpu_rail_off_ready(void)
+{
+ unsigned int cpu_rst_status;
+
+ cpu_rst_status = readl(clk_base +
+ TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+
+ return !!(cpu_rst_status & 0x2);
+}
+
+static void tegra20_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra20_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_SOURCE_CSITE);
+ writel(3<<30, clk_base + CLK_SOURCE_CSITE);
+
+ tegra20_cpu_clk_sctx.cpu_burst =
+ readl(clk_base + CCLK_BURST_POLICY);
+ tegra20_cpu_clk_sctx.pllx_base =
+ readl(clk_base + PLLX_BASE);
+ tegra20_cpu_clk_sctx.pllx_misc =
+ readl(clk_base + PLLX_MISC);
+ tegra20_cpu_clk_sctx.cclk_divider =
+ readl(clk_base + SUPER_CCLK_DIVIDER);
+}
+
+static void tegra20_cpu_clock_resume(void)
+{
+ unsigned int reg, policy;
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(clk_base + CCLK_BURST_POLICY);
+ policy = (reg >> CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+ if (policy == CCLK_IDLE_POLICY)
+ reg = (reg >> CCLK_IDLE_POLICY_SHIFT) & 0xF;
+ else if (policy == CCLK_RUN_POLICY)
+ reg = (reg >> CCLK_RUN_POLICY_SHIFT) & 0xF;
+ else
+ BUG();
+
+ if (reg != CCLK_BURST_POLICY_PLLX) {
+ /* restore PLLX settings if CPU is on different PLL */
+ writel(tegra20_cpu_clk_sctx.pllx_misc,
+ clk_base + PLLX_MISC);
+ writel(tegra20_cpu_clk_sctx.pllx_base,
+ clk_base + PLLX_BASE);
+
+ /* wait for PLL stabilization if PLLX was enabled */
+ if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30))
+ udelay(300);
+ }
+
+ /*
+ * Restore original burst policy setting for calls resulting from CPU
+ * LP2 in idle or system suspend.
+ */
+ writel(tegra20_cpu_clk_sctx.cclk_divider,
+ clk_base + SUPER_CCLK_DIVIDER);
+ writel(tegra20_cpu_clk_sctx.cpu_burst,
+ clk_base + CCLK_BURST_POLICY);
+
+ writel(tegra20_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_SOURCE_CSITE);
+}
+#endif
+
+static struct tegra_cpu_car_ops tegra20_cpu_car_ops = {
+ .wait_for_reset = tegra20_wait_cpu_in_reset,
+ .put_in_reset = tegra20_put_cpu_in_reset,
+ .out_of_reset = tegra20_cpu_out_of_reset,
+ .enable_clock = tegra20_enable_cpu_clock,
+ .disable_clock = tegra20_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .rail_off_ready = tegra20_cpu_rail_off_ready,
+ .suspend = tegra20_cpu_clock_suspend,
+ .resume = tegra20_cpu_clock_resume,
+#endif
+};
+
+static __initdata struct tegra_clk_init_table init_table[] = {
+ {pll_p, clk_max, 216000000, 1},
+ {pll_p_out1, clk_max, 28800000, 1},
+ {pll_p_out2, clk_max, 48000000, 1},
+ {pll_p_out3, clk_max, 72000000, 1},
+ {pll_p_out4, clk_max, 24000000, 1},
+ {pll_c, clk_max, 600000000, 1},
+ {pll_c_out1, clk_max, 120000000, 1},
+ {sclk, pll_c_out1, 0, 1},
+ {hclk, clk_max, 0, 1},
+ {pclk, clk_max, 60000000, 1},
+ {csite, clk_max, 0, 1},
+ {emc, clk_max, 0, 1},
+ {cclk, clk_max, 0, 1},
+ {uarta, pll_p, 0, 0},
+ {uartb, pll_p, 0, 0},
+ {uartc, pll_p, 0, 0},
+ {uartd, pll_p, 0, 0},
+ {uarte, pll_p, 0, 0},
+ {pll_a, clk_max, 56448000, 1},
+ {pll_a_out0, clk_max, 11289600, 1},
+ {cdev1, clk_max, 0, 1},
+ {blink, clk_max, 32768, 1},
+ {i2s1, pll_a_out0, 11289600, 0},
+ {i2s2, pll_a_out0, 11289600, 0},
+ {sdmmc1, pll_p, 48000000, 0},
+ {sdmmc3, pll_p, 48000000, 0},
+ {sdmmc4, pll_p, 48000000, 0},
+ {spi, pll_p, 20000000, 0},
+ {sbc1, pll_p, 100000000, 0},
+ {sbc2, pll_p, 100000000, 0},
+ {sbc3, pll_p, 100000000, 0},
+ {sbc4, pll_p, 100000000, 0},
+ {host1x, pll_c, 150000000, 0},
+ {disp1, pll_p, 600000000, 0},
+ {disp2, pll_p, 600000000, 0},
+ {gr2d, pll_c, 300000000, 0},
+ {gr3d, pll_c, 300000000, 0},
+ {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */
+};
+
+static void __init tegra20_clock_apply_init_table(void)
+{
+ tegra_init_from_table(init_table, clks, clk_max);
+}
+
+/*
+ * Some clocks may be used by different drivers depending on the board
+ * configuration. List those here to register them twice in the clock lookup
+ * table under two names.
+ */
+static struct tegra_clk_duplicate tegra_clk_duplicates[] = {
+ TEGRA_CLK_DUPLICATE(usbd, "utmip-pad", NULL),
+ TEGRA_CLK_DUPLICATE(usbd, "tegra-ehci.0", NULL),
+ TEGRA_CLK_DUPLICATE(usbd, "tegra-otg", NULL),
+ TEGRA_CLK_DUPLICATE(cclk, NULL, "cpu"),
+ TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* Must be the last entry */
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra20-pmc" },
+ {},
+};
+
+void __init tegra20_clock_init(struct device_node *np)
+{
+ int i;
+ struct device_node *node;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("Can't map CAR registers\n");
+ BUG();
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ BUG();
+ }
+
+ pmc_base = of_iomap(node, 0);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ BUG();
+ }
+
+ tegra20_osc_clk_init();
+ tegra20_pmc_clk_init();
+ tegra20_fixed_clk_init();
+ tegra20_pll_init();
+ tegra20_super_clk_init();
+ tegra20_periph_clk_init();
+ tegra20_audio_clk_init();
+
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+ if (IS_ERR(clks[i])) {
+ pr_err("Tegra20 clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ BUG();
+ }
+ if (!clks[i])
+ clks[i] = ERR_PTR(-EINVAL);
+ }
+
+ tegra_init_dup_clks(tegra_clk_duplicates, clks, clk_max);
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ tegra_clk_apply_init_table = tegra20_clock_apply_init_table;
+
+ tegra_cpu_car_ops = &tegra20_cpu_car_ops;
+}
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
new file mode 100644
index 000000000..ba99e3844
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -0,0 +1,2007 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk/tegra.h>
+#include <linux/tegra-powergate.h>
+
+#include "clk.h"
+
+#define RST_DEVICES_L 0x004
+#define RST_DEVICES_H 0x008
+#define RST_DEVICES_U 0x00c
+#define RST_DEVICES_V 0x358
+#define RST_DEVICES_W 0x35c
+#define RST_DEVICES_SET_L 0x300
+#define RST_DEVICES_CLR_L 0x304
+#define RST_DEVICES_SET_H 0x308
+#define RST_DEVICES_CLR_H 0x30c
+#define RST_DEVICES_SET_U 0x310
+#define RST_DEVICES_CLR_U 0x314
+#define RST_DEVICES_SET_V 0x430
+#define RST_DEVICES_CLR_V 0x434
+#define RST_DEVICES_SET_W 0x438
+#define RST_DEVICES_CLR_W 0x43c
+#define RST_DEVICES_NUM 5
+
+#define CLK_OUT_ENB_L 0x010
+#define CLK_OUT_ENB_H 0x014
+#define CLK_OUT_ENB_U 0x018
+#define CLK_OUT_ENB_V 0x360
+#define CLK_OUT_ENB_W 0x364
+#define CLK_OUT_ENB_SET_L 0x320
+#define CLK_OUT_ENB_CLR_L 0x324
+#define CLK_OUT_ENB_SET_H 0x328
+#define CLK_OUT_ENB_CLR_H 0x32c
+#define CLK_OUT_ENB_SET_U 0x330
+#define CLK_OUT_ENB_CLR_U 0x334
+#define CLK_OUT_ENB_SET_V 0x440
+#define CLK_OUT_ENB_CLR_V 0x444
+#define CLK_OUT_ENB_SET_W 0x448
+#define CLK_OUT_ENB_CLR_W 0x44c
+#define CLK_OUT_ENB_NUM 5
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_MASK (0xF<<28)
+#define OSC_CTRL_OSC_FREQ_13MHZ (0X0<<28)
+#define OSC_CTRL_OSC_FREQ_19_2MHZ (0X4<<28)
+#define OSC_CTRL_OSC_FREQ_12MHZ (0X8<<28)
+#define OSC_CTRL_OSC_FREQ_26MHZ (0XC<<28)
+#define OSC_CTRL_OSC_FREQ_16_8MHZ (0X1<<28)
+#define OSC_CTRL_OSC_FREQ_38_4MHZ (0X5<<28)
+#define OSC_CTRL_OSC_FREQ_48MHZ (0X9<<28)
+#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
+
+#define OSC_CTRL_PLL_REF_DIV_MASK (3<<26)
+#define OSC_CTRL_PLL_REF_DIV_1 (0<<26)
+#define OSC_CTRL_PLL_REF_DIV_2 (1<<26)
+#define OSC_CTRL_PLL_REF_DIV_4 (2<<26)
+
+#define OSC_FREQ_DET 0x58
+#define OSC_FREQ_DET_TRIG BIT(31)
+
+#define OSC_FREQ_DET_STATUS 0x5c
+#define OSC_FREQ_DET_BUSY BIT(31)
+#define OSC_FREQ_DET_CNT_MASK 0xffff
+
+#define CCLKG_BURST_POLICY 0x368
+#define SUPER_CCLKG_DIVIDER 0x36c
+#define CCLKLP_BURST_POLICY 0x370
+#define SUPER_CCLKLP_DIVIDER 0x374
+#define SCLK_BURST_POLICY 0x028
+#define SUPER_SCLK_DIVIDER 0x02c
+
+#define SYSTEM_CLK_RATE 0x030
+
+#define PLLC_BASE 0x80
+#define PLLC_MISC 0x8c
+#define PLLM_BASE 0x90
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC 0x4bc
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+
+#define PLLE_AUX 0x48c
+#define PLLC_OUT 0x84
+#define PLLM_OUT 0x94
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
+#define PLLA_OUT 0xb4
+
+#define AUDIO_SYNC_CLK_I2S0 0x4a0
+#define AUDIO_SYNC_CLK_I2S1 0x4a4
+#define AUDIO_SYNC_CLK_I2S2 0x4a8
+#define AUDIO_SYNC_CLK_I2S3 0x4ac
+#define AUDIO_SYNC_CLK_I2S4 0x4b0
+#define AUDIO_SYNC_CLK_SPDIF 0x4b4
+
+#define PMC_CLK_OUT_CNTRL 0x1a8
+
+#define CLK_SOURCE_I2S0 0x1d8
+#define CLK_SOURCE_I2S1 0x100
+#define CLK_SOURCE_I2S2 0x104
+#define CLK_SOURCE_I2S3 0x3bc
+#define CLK_SOURCE_I2S4 0x3c0
+#define CLK_SOURCE_SPDIF_OUT 0x108
+#define CLK_SOURCE_SPDIF_IN 0x10c
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_D_AUDIO 0x3d0
+#define CLK_SOURCE_DAM0 0x3d8
+#define CLK_SOURCE_DAM1 0x3dc
+#define CLK_SOURCE_DAM2 0x3e0
+#define CLK_SOURCE_HDA 0x428
+#define CLK_SOURCE_HDA2CODEC_2X 0x3e4
+#define CLK_SOURCE_SBC1 0x134
+#define CLK_SOURCE_SBC2 0x118
+#define CLK_SOURCE_SBC3 0x11c
+#define CLK_SOURCE_SBC4 0x1b4
+#define CLK_SOURCE_SBC5 0x3c8
+#define CLK_SOURCE_SBC6 0x3cc
+#define CLK_SOURCE_SATA_OOB 0x420
+#define CLK_SOURCE_SATA 0x424
+#define CLK_SOURCE_NDFLASH 0x160
+#define CLK_SOURCE_NDSPEED 0x3f8
+#define CLK_SOURCE_VFIR 0x168
+#define CLK_SOURCE_SDMMC1 0x150
+#define CLK_SOURCE_SDMMC2 0x154
+#define CLK_SOURCE_SDMMC3 0x1bc
+#define CLK_SOURCE_SDMMC4 0x164
+#define CLK_SOURCE_VDE 0x1c8
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_LA 0x1f8
+#define CLK_SOURCE_OWR 0x1cc
+#define CLK_SOURCE_NOR 0x1d0
+#define CLK_SOURCE_MIPI 0x174
+#define CLK_SOURCE_I2C1 0x124
+#define CLK_SOURCE_I2C2 0x198
+#define CLK_SOURCE_I2C3 0x1b8
+#define CLK_SOURCE_I2C4 0x3c4
+#define CLK_SOURCE_I2C5 0x128
+#define CLK_SOURCE_UARTA 0x178
+#define CLK_SOURCE_UARTB 0x17c
+#define CLK_SOURCE_UARTC 0x1a0
+#define CLK_SOURCE_UARTD 0x1c0
+#define CLK_SOURCE_UARTE 0x1c4
+#define CLK_SOURCE_VI 0x148
+#define CLK_SOURCE_VI_SENSOR 0x1a8
+#define CLK_SOURCE_3D 0x158
+#define CLK_SOURCE_3D2 0x3b0
+#define CLK_SOURCE_2D 0x15c
+#define CLK_SOURCE_EPP 0x16c
+#define CLK_SOURCE_MPE 0x170
+#define CLK_SOURCE_HOST1X 0x180
+#define CLK_SOURCE_CVE 0x140
+#define CLK_SOURCE_TVO 0x188
+#define CLK_SOURCE_DTV 0x1dc
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_TVDAC 0x194
+#define CLK_SOURCE_DISP1 0x138
+#define CLK_SOURCE_DISP2 0x13c
+#define CLK_SOURCE_DSIB 0xd0
+#define CLK_SOURCE_TSENSOR 0x3b8
+#define CLK_SOURCE_ACTMON 0x3e8
+#define CLK_SOURCE_EXTERN1 0x3ec
+#define CLK_SOURCE_EXTERN2 0x3f0
+#define CLK_SOURCE_EXTERN3 0x3f4
+#define CLK_SOURCE_I2CSLOW 0x3fc
+#define CLK_SOURCE_SE 0x42c
+#define CLK_SOURCE_MSELECT 0x3b4
+#define CLK_SOURCE_EMC 0x19c
+
+#define AUDIO_SYNC_DOUBLER 0x49c
+
+#define PMC_CTRL 0
+#define PMC_CTRL_BLINK_ENB 7
+
+#define PMC_DPD_PADS_ORIDE 0x1c
+#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20
+#define PMC_BLINK_TIMER 0x40
+
+#define UTMIP_PLL_CFG2 0x488
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6)
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4)
+
+#define UTMIP_PLL_CFG1 0x484
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6)
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16)
+
+/* Tegra CPU clock and reset control regs */
+#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344
+#define TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR 0x34c
+#define TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470
+
+#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
+#define CPU_RESET(cpu) (0x1111ul << (cpu))
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_PLLX_BASE 0xe0
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define CLK_RESET_SOURCE_CSITE 0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4
+#define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0
+#define CLK_RESET_CCLK_IDLE_POLICY 1
+#define CLK_RESET_CCLK_RUN_POLICY 2
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 pllx_misc;
+ u32 pllx_base;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+} tegra30_cpu_clk_sctx;
+#endif
+
+static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32];
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+static unsigned long input_freq;
+
+static DEFINE_SPINLOCK(clk_doubler_lock);
+static DEFINE_SPINLOCK(clk_out_lock);
+static DEFINE_SPINLOCK(pll_div_lock);
+static DEFINE_SPINLOCK(cml_lock);
+static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(sysrate_lock);
+
+#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_DIV16(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP, \
+ _regs, _clk_num, periph_clk_enb_refcnt, \
+ _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 29, 3, 0, 0, 8, 1, 0, _regs, _clk_num, \
+ periph_clk_enb_refcnt, _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \
+ _clk_num, _regs, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id)
+
+#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\
+ _clk_num, _regs, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ 30, 2, 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs, \
+ _clk_num, periph_clk_enb_refcnt, 0, _clk_id)
+
+#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \
+ _mux_shift, _mux_width, _clk_num, _regs, \
+ _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \
+ _mux_shift, _mux_width, 0, 0, 0, 0, 0, _regs, \
+ _clk_num, periph_clk_enb_refcnt, _gate_flags, \
+ _clk_id)
+
+/*
+ * IDs assigned here must be in sync with DT bindings definition
+ * for Tegra30 clocks.
+ */
+enum tegra30_clk {
+ cpu, rtc = 4, timer, uarta, gpio = 8, sdmmc2, i2s1 = 11, i2c1, ndflash,
+ sdmmc1, sdmmc4, pwm = 17, i2s2, epp, gr2d = 21, usbd, isp, gr3d,
+ disp2 = 26, disp1, host1x, vcp, i2s0, cop_cache, mc, ahbdma, apbdma,
+ kbc = 36, statmon, pmc, kfuse = 40, sbc1, nor, sbc2 = 44, sbc3 = 46,
+ i2c5, dsia, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2,
+ usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3,
+ pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2cslow,
+ dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92,
+ cdev2, cdev1, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4,
+ i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x,
+ atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x,
+ spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda,
+ se = 127, hda2hdmi, sata_cold, uartb = 160, vfir, spdif_in, spdif_out,
+ vi, vi_sensor, fuse, fuse_burn, cve, tvo, clk_32k, clk_m, clk_m_div2,
+ clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_m, pll_m_out1, pll_p,
+ pll_p_out1, pll_p_out2, pll_p_out3, pll_p_out4, pll_a, pll_a_out0,
+ pll_d, pll_d_out0, pll_d2, pll_d2_out0, pll_u, pll_x, pll_x_out0, pll_e,
+ spdif_in_sync, i2s0_sync, i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync,
+ vimclk_sync, audio0, audio1, audio2, audio3, audio4, spdif, clk_out_1,
+ clk_out_2, clk_out_3, sclk, blink, cclk_g, cclk_lp, twd, cml0, cml1,
+ hclk, pclk, clk_out_1_mux = 300, clk_max
+};
+
+static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
+
+/*
+ * Structure defining the fields for USB UTMI clocks Parameters.
+ */
+struct utmi_clk_param {
+ /* Oscillator Frequency in KHz */
+ u32 osc_frequency;
+ /* UTMIP PLL Enable Delay Count */
+ u8 enable_delay_count;
+ /* UTMIP PLL Stable count */
+ u8 stable_count;
+ /* UTMIP PLL Active delay count */
+ u8 active_delay_count;
+ /* UTMIP PLL Xtal frequency count */
+ u8 xtal_freq_count;
+};
+
+static const struct utmi_clk_param utmi_parameters[] = {
+/* OSC_FREQUENCY, ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */
+ {13000000, 0x02, 0x33, 0x05, 0x7F},
+ {19200000, 0x03, 0x4B, 0x06, 0xBB},
+ {12000000, 0x02, 0x2F, 0x04, 0x76},
+ {26000000, 0x04, 0x66, 0x09, 0xFE},
+ {16800000, 0x03, 0x41, 0x0A, 0xA4},
+};
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 1040000000, 520, 6, 0, 8},
+ { 13000000, 1040000000, 480, 6, 0, 8},
+ { 16800000, 1040000000, 495, 8, 0, 8}, /* actual: 1039.5 MHz */
+ { 19200000, 1040000000, 325, 6, 0, 6},
+ { 26000000, 1040000000, 520, 13, 0, 8},
+
+ { 12000000, 832000000, 416, 6, 0, 8},
+ { 13000000, 832000000, 832, 13, 0, 8},
+ { 16800000, 832000000, 396, 8, 0, 8}, /* actual: 831.6 MHz */
+ { 19200000, 832000000, 260, 6, 0, 8},
+ { 26000000, 832000000, 416, 13, 0, 8},
+
+ { 12000000, 624000000, 624, 12, 0, 8},
+ { 13000000, 624000000, 624, 13, 0, 8},
+ { 16800000, 600000000, 520, 14, 0, 8},
+ { 19200000, 624000000, 520, 16, 0, 8},
+ { 26000000, 624000000, 624, 26, 0, 8},
+
+ { 12000000, 600000000, 600, 12, 0, 8},
+ { 13000000, 600000000, 600, 13, 0, 8},
+ { 16800000, 600000000, 500, 14, 0, 8},
+ { 19200000, 600000000, 375, 12, 0, 6},
+ { 26000000, 600000000, 600, 26, 0, 8},
+
+ { 12000000, 520000000, 520, 12, 0, 8},
+ { 13000000, 520000000, 520, 13, 0, 8},
+ { 16800000, 520000000, 495, 16, 0, 8}, /* actual: 519.75 MHz */
+ { 19200000, 520000000, 325, 12, 0, 6},
+ { 26000000, 520000000, 520, 26, 0, 8},
+
+ { 12000000, 416000000, 416, 12, 0, 8},
+ { 13000000, 416000000, 416, 13, 0, 8},
+ { 16800000, 416000000, 396, 16, 0, 8}, /* actual: 415.8 MHz */
+ { 19200000, 416000000, 260, 12, 0, 6},
+ { 26000000, 416000000, 416, 26, 0, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 666000000, 666, 12, 0, 8},
+ { 13000000, 666000000, 666, 13, 0, 8},
+ { 16800000, 666000000, 555, 14, 0, 8},
+ { 19200000, 666000000, 555, 16, 0, 8},
+ { 26000000, 666000000, 666, 26, 0, 8},
+ { 12000000, 600000000, 600, 12, 0, 8},
+ { 13000000, 600000000, 600, 13, 0, 8},
+ { 16800000, 600000000, 500, 14, 0, 8},
+ { 19200000, 600000000, 375, 12, 0, 6},
+ { 26000000, 600000000, 600, 26, 0, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 1, 8},
+ { 13000000, 216000000, 432, 13, 1, 8},
+ { 16800000, 216000000, 360, 14, 1, 8},
+ { 19200000, 216000000, 360, 16, 1, 8},
+ { 26000000, 216000000, 432, 26, 1, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 9600000, 564480000, 294, 5, 0, 4},
+ { 9600000, 552960000, 288, 5, 0, 4},
+ { 9600000, 24000000, 5, 2, 0, 1},
+
+ { 28800000, 56448000, 49, 25, 0, 1},
+ { 28800000, 73728000, 64, 25, 0, 1},
+ { 28800000, 24000000, 5, 6, 0, 1},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 216000000, 216, 12, 0, 4},
+ { 13000000, 216000000, 216, 13, 0, 4},
+ { 16800000, 216000000, 180, 14, 0, 4},
+ { 19200000, 216000000, 180, 16, 0, 4},
+ { 26000000, 216000000, 216, 26, 0, 4},
+
+ { 12000000, 594000000, 594, 12, 0, 8},
+ { 13000000, 594000000, 594, 13, 0, 8},
+ { 16800000, 594000000, 495, 14, 0, 8},
+ { 19200000, 594000000, 495, 16, 0, 8},
+ { 26000000, 594000000, 594, 26, 0, 8},
+
+ { 12000000, 1000000000, 1000, 12, 0, 12},
+ { 13000000, 1000000000, 1000, 13, 0, 12},
+ { 19200000, 1000000000, 625, 12, 0, 8},
+ { 26000000, 1000000000, 1000, 26, 0, 12},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 0, 12},
+ { 13000000, 480000000, 960, 13, 0, 12},
+ { 16800000, 480000000, 400, 7, 0, 5},
+ { 19200000, 480000000, 200, 4, 0, 3},
+ { 26000000, 480000000, 960, 26, 0, 12},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1.7 GHz */
+ { 12000000, 1700000000, 850, 6, 0, 8},
+ { 13000000, 1700000000, 915, 7, 0, 8}, /* actual: 1699.2 MHz */
+ { 16800000, 1700000000, 708, 7, 0, 8}, /* actual: 1699.2 MHz */
+ { 19200000, 1700000000, 885, 10, 0, 8}, /* actual: 1699.2 MHz */
+ { 26000000, 1700000000, 850, 13, 0, 8},
+
+ /* 1.6 GHz */
+ { 12000000, 1600000000, 800, 6, 0, 8},
+ { 13000000, 1600000000, 738, 6, 0, 8}, /* actual: 1599.0 MHz */
+ { 16800000, 1600000000, 857, 9, 0, 8}, /* actual: 1599.7 MHz */
+ { 19200000, 1600000000, 500, 6, 0, 8},
+ { 26000000, 1600000000, 800, 13, 0, 8},
+
+ /* 1.5 GHz */
+ { 12000000, 1500000000, 750, 6, 0, 8},
+ { 13000000, 1500000000, 923, 8, 0, 8}, /* actual: 1499.8 MHz */
+ { 16800000, 1500000000, 625, 7, 0, 8},
+ { 19200000, 1500000000, 625, 8, 0, 8},
+ { 26000000, 1500000000, 750, 13, 0, 8},
+
+ /* 1.4 GHz */
+ { 12000000, 1400000000, 700, 6, 0, 8},
+ { 13000000, 1400000000, 969, 9, 0, 8}, /* actual: 1399.7 MHz */
+ { 16800000, 1400000000, 1000, 12, 0, 8},
+ { 19200000, 1400000000, 875, 12, 0, 8},
+ { 26000000, 1400000000, 700, 13, 0, 8},
+
+ /* 1.3 GHz */
+ { 12000000, 1300000000, 975, 9, 0, 8},
+ { 13000000, 1300000000, 1000, 10, 0, 8},
+ { 16800000, 1300000000, 928, 12, 0, 8}, /* actual: 1299.2 MHz */
+ { 19200000, 1300000000, 812, 12, 0, 8}, /* actual: 1299.2 MHz */
+ { 26000000, 1300000000, 650, 13, 0, 8},
+
+ /* 1.2 GHz */
+ { 12000000, 1200000000, 1000, 10, 0, 8},
+ { 13000000, 1200000000, 923, 10, 0, 8}, /* actual: 1199.9 MHz */
+ { 16800000, 1200000000, 1000, 14, 0, 8},
+ { 19200000, 1200000000, 1000, 16, 0, 8},
+ { 26000000, 1200000000, 600, 13, 0, 8},
+
+ /* 1.1 GHz */
+ { 12000000, 1100000000, 825, 9, 0, 8},
+ { 13000000, 1100000000, 846, 10, 0, 8}, /* actual: 1099.8 MHz */
+ { 16800000, 1100000000, 982, 15, 0, 8}, /* actual: 1099.8 MHz */
+ { 19200000, 1100000000, 859, 15, 0, 8}, /* actual: 1099.5 MHz */
+ { 26000000, 1100000000, 550, 13, 0, 8},
+
+ /* 1 GHz */
+ { 12000000, 1000000000, 1000, 12, 0, 8},
+ { 13000000, 1000000000, 1000, 13, 0, 8},
+ { 16800000, 1000000000, 833, 14, 0, 8}, /* actual: 999.6 MHz */
+ { 19200000, 1000000000, 625, 12, 0, 8},
+ { 26000000, 1000000000, 1000, 26, 0, 8},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 12000000, 100000000, 150, 1, 18, 11},
+ { 216000000, 100000000, 200, 18, 24, 13},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+/* PLL parameters */
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+};
+
+static struct tegra_clk_pll_params pll_d2_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+};
+
+static struct tegra_clk_pll_params pll_u_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 48000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1700000000,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 216000000,
+ .cf_min = 12000000,
+ .cf_max = 12000000,
+ .vco_min = 1200000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+};
+
+/* Peripheral clock registers */
+static struct tegra_clk_periph_regs periph_l_regs = {
+ .enb_reg = CLK_OUT_ENB_L,
+ .enb_set_reg = CLK_OUT_ENB_SET_L,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_L,
+ .rst_reg = RST_DEVICES_L,
+ .rst_set_reg = RST_DEVICES_SET_L,
+ .rst_clr_reg = RST_DEVICES_CLR_L,
+};
+
+static struct tegra_clk_periph_regs periph_h_regs = {
+ .enb_reg = CLK_OUT_ENB_H,
+ .enb_set_reg = CLK_OUT_ENB_SET_H,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_H,
+ .rst_reg = RST_DEVICES_H,
+ .rst_set_reg = RST_DEVICES_SET_H,
+ .rst_clr_reg = RST_DEVICES_CLR_H,
+};
+
+static struct tegra_clk_periph_regs periph_u_regs = {
+ .enb_reg = CLK_OUT_ENB_U,
+ .enb_set_reg = CLK_OUT_ENB_SET_U,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_U,
+ .rst_reg = RST_DEVICES_U,
+ .rst_set_reg = RST_DEVICES_SET_U,
+ .rst_clr_reg = RST_DEVICES_CLR_U,
+};
+
+static struct tegra_clk_periph_regs periph_v_regs = {
+ .enb_reg = CLK_OUT_ENB_V,
+ .enb_set_reg = CLK_OUT_ENB_SET_V,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_V,
+ .rst_reg = RST_DEVICES_V,
+ .rst_set_reg = RST_DEVICES_SET_V,
+ .rst_clr_reg = RST_DEVICES_CLR_V,
+};
+
+static struct tegra_clk_periph_regs periph_w_regs = {
+ .enb_reg = CLK_OUT_ENB_W,
+ .enb_set_reg = CLK_OUT_ENB_SET_W,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_W,
+ .rst_reg = RST_DEVICES_W,
+ .rst_set_reg = RST_DEVICES_SET_W,
+ .rst_clr_reg = RST_DEVICES_CLR_W,
+};
+
+static void tegra30_clk_measure_input_freq(void)
+{
+ u32 osc_ctrl = readl_relaxed(clk_base + OSC_CTRL);
+ u32 auto_clk_control = osc_ctrl & OSC_CTRL_OSC_FREQ_MASK;
+ u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
+
+ switch (auto_clk_control) {
+ case OSC_CTRL_OSC_FREQ_12MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 12000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_13MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 13000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_19_2MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 19200000;
+ break;
+ case OSC_CTRL_OSC_FREQ_26MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 26000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_16_8MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 16800000;
+ break;
+ case OSC_CTRL_OSC_FREQ_38_4MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_2);
+ input_freq = 38400000;
+ break;
+ case OSC_CTRL_OSC_FREQ_48MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_4);
+ input_freq = 48000000;
+ break;
+ default:
+ pr_err("Unexpected auto clock control value %d",
+ auto_clk_control);
+ BUG();
+ return;
+ }
+}
+
+static unsigned int tegra30_get_pll_ref_div(void)
+{
+ u32 pll_ref_div = readl_relaxed(clk_base + OSC_CTRL) &
+ OSC_CTRL_PLL_REF_DIV_MASK;
+
+ switch (pll_ref_div) {
+ case OSC_CTRL_PLL_REF_DIV_1:
+ return 1;
+ case OSC_CTRL_PLL_REF_DIV_2:
+ return 2;
+ case OSC_CTRL_PLL_REF_DIV_4:
+ return 4;
+ default:
+ pr_err("Invalid pll ref divider %d", pll_ref_div);
+ BUG();
+ }
+ return 0;
+}
+
+static void tegra30_utmi_param_configure(void)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
+ if (input_freq == utmi_parameters[i].osc_frequency)
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(utmi_parameters)) {
+ pr_err("%s: Unexpected input rate %lu\n", __func__, input_freq);
+ return;
+ }
+
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL stable and active counts */
+ reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ reg |= UTMIP_PLL_CFG2_STABLE_COUNT(
+ utmi_parameters[i].stable_count);
+
+ reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+
+ reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(
+ utmi_parameters[i].active_delay_count);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL delay and oscillator frequency counts */
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1);
+ reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+
+ reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(
+ utmi_parameters[i].enable_delay_count);
+
+ reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(
+ utmi_parameters[i].xtal_freq_count);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1);
+}
+
+static const char *pll_e_parents[] = {"pll_ref", "pll_p"};
+
+static void __init tegra30_pll_init(void)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
+ 0, &pll_c_params,
+ TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK,
+ pll_c_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_c", NULL);
+ clks[pll_c] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0, CLK_SET_RATE_PARENT,
+ 0, NULL);
+ clk_register_clkdev(clk, "pll_c_out1", NULL);
+ clks[pll_c_out1] = clk;
+
+ /* PLLP */
+ clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc_base, 0,
+ 408000000, &pll_p_params,
+ TEGRA_PLL_FIXED | TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_USE_LOCK, pll_p_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_p", NULL);
+ clks[pll_p] = clk;
+
+ /* PLLP_OUT1 */
+ clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p",
+ clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 8, 8, 1,
+ &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div",
+ clk_base + PLLP_OUTA, 1, 0,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out1", NULL);
+ clks[pll_p_out1] = clk;
+
+ /* PLLP_OUT2 */
+ clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p",
+ clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 24, 8, 1,
+ &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div",
+ clk_base + PLLP_OUTA, 17, 16,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out2", NULL);
+ clks[pll_p_out2] = clk;
+
+ /* PLLP_OUT3 */
+ clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p",
+ clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 8, 8, 1,
+ &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div",
+ clk_base + PLLP_OUTB, 1, 0,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out3", NULL);
+ clks[pll_p_out3] = clk;
+
+ /* PLLP_OUT4 */
+ clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p",
+ clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED |
+ TEGRA_DIVIDER_ROUND_UP, 24, 8, 1,
+ &pll_div_lock);
+ clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div",
+ clk_base + PLLP_OUTB, 17, 16,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ &pll_div_lock);
+ clk_register_clkdev(clk, "pll_p_out4", NULL);
+ clks[pll_p_out4] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0,
+ &pll_m_params, TEGRA_PLLM | TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_SET_DCCON | TEGRA_PLL_USE_LOCK,
+ pll_m_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_m", NULL);
+ clks[pll_m] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_m_out1", NULL);
+ clks[pll_m_out1] = clk;
+
+ /* PLLX */
+ clk = tegra_clk_register_pll("pll_x", "pll_ref", clk_base, pmc_base, 0,
+ 0, &pll_x_params, TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_SET_DCCON | TEGRA_PLL_USE_LOCK,
+ pll_x_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_x", NULL);
+ clks[pll_x] = clk;
+
+ /* PLLX_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_x_out0", NULL);
+ clks[pll_x_out0] = clk;
+
+ /* PLLU */
+ clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc_base, 0,
+ 0, &pll_u_params, TEGRA_PLLU | TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK,
+ pll_u_freq_table,
+ NULL);
+ clk_register_clkdev(clk, "pll_u", NULL);
+ clks[pll_u] = clk;
+
+ tegra30_utmi_param_configure();
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc_base, 0,
+ 0, &pll_d_params, TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK,
+ pll_d_freq_table, &pll_d_lock);
+ clk_register_clkdev(clk, "pll_d", NULL);
+ clks[pll_d] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d_out0", NULL);
+ clks[pll_d_out0] = clk;
+
+ /* PLLD2 */
+ clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc_base, 0,
+ 0, &pll_d2_params, TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK,
+ pll_d_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_d2", NULL);
+ clks[pll_d2] = clk;
+
+ /* PLLD2_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d2_out0", NULL);
+ clks[pll_d2_out0] = clk;
+
+ /* PLLA */
+ clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc_base,
+ 0, 0, &pll_a_params, TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_a", NULL);
+ clks[pll_a] = clk;
+
+ /* PLLA_OUT0 */
+ clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
+ clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
+ clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_a_out0", NULL);
+ clks[pll_a_out0] = clk;
+
+ /* PLLE */
+ clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents,
+ ARRAY_SIZE(pll_e_parents), 0,
+ clk_base + PLLE_AUX, 2, 1, 0, NULL);
+ clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
+ CLK_GET_RATE_NOCACHE, 100000000, &pll_e_params,
+ TEGRA_PLLE_CONFIGURE, pll_e_freq_table, NULL);
+ clk_register_clkdev(clk, "pll_e", NULL);
+ clks[pll_e] = clk;
+}
+
+static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync",
+ "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",};
+static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2",
+ "clk_m_div4", "extern1", };
+static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2",
+ "clk_m_div4", "extern2", };
+static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2",
+ "clk_m_div4", "extern3", };
+
+static void __init tegra30_audio_clk_init(void)
+{
+ struct clk *clk;
+
+ /* spdif_in_sync */
+ clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000,
+ 24000000);
+ clk_register_clkdev(clk, "spdif_in_sync", NULL);
+ clks[spdif_in_sync] = clk;
+
+ /* i2s0_sync */
+ clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s0_sync", NULL);
+ clks[i2s0_sync] = clk;
+
+ /* i2s1_sync */
+ clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s1_sync", NULL);
+ clks[i2s1_sync] = clk;
+
+ /* i2s2_sync */
+ clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s2_sync", NULL);
+ clks[i2s2_sync] = clk;
+
+ /* i2s3_sync */
+ clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s3_sync", NULL);
+ clks[i2s3_sync] = clk;
+
+ /* i2s4_sync */
+ clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "i2s4_sync", NULL);
+ clks[i2s4_sync] = clk;
+
+ /* vimclk_sync */
+ clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000);
+ clk_register_clkdev(clk, "vimclk_sync", NULL);
+ clks[vimclk_sync] = clk;
+
+ /* audio0 */
+ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S0, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio0", NULL);
+ clks[audio0] = clk;
+
+ /* audio1 */
+ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S1, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio1", NULL);
+ clks[audio1] = clk;
+
+ /* audio2 */
+ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S2, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio2", NULL);
+ clks[audio2] = clk;
+
+ /* audio3 */
+ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S3, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio3", NULL);
+ clks[audio3] = clk;
+
+ /* audio4 */
+ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_I2S4, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "audio4", NULL);
+ clks[audio4] = clk;
+
+ /* spdif */
+ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk), 0,
+ clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0,
+ clk_base + AUDIO_SYNC_CLK_SPDIF, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clk_register_clkdev(clk, "spdif", NULL);
+ clks[spdif] = clk;
+
+ /* audio0_2x */
+ clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio0_div", "audio0_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 113, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio0_2x", NULL);
+ clks[audio0_2x] = clk;
+
+ /* audio1_2x */
+ clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio1_div", "audio1_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 114, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio1_2x", NULL);
+ clks[audio1_2x] = clk;
+
+ /* audio2_2x */
+ clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio2_div", "audio2_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 115, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio2_2x", NULL);
+ clks[audio2_2x] = clk;
+
+ /* audio3_2x */
+ clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio3_div", "audio3_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 116, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio3_2x", NULL);
+ clks[audio3_2x] = clk;
+
+ /* audio4_2x */
+ clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("audio4_div", "audio4_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 117, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "audio4_2x", NULL);
+ clks[audio4_2x] = clk;
+
+ /* spdif_2x */
+ clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider("spdif_div", "spdif_doubler",
+ clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 118, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "spdif_2x", NULL);
+ clks[spdif_2x] = clk;
+}
+
+static void __init tegra30_pmc_clk_init(void)
+{
+ struct clk *clk;
+
+ /* clk_out_1 */
+ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents,
+ ARRAY_SIZE(clk_out1_parents), 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0,
+ &clk_out_lock);
+ clks[clk_out_1_mux] = clk;
+ clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 2, 0,
+ &clk_out_lock);
+ clk_register_clkdev(clk, "extern1", "clk_out_1");
+ clks[clk_out_1] = clk;
+
+ /* clk_out_2 */
+ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents,
+ ARRAY_SIZE(clk_out1_parents), 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0,
+ &clk_out_lock);
+ clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 10, 0,
+ &clk_out_lock);
+ clk_register_clkdev(clk, "extern2", "clk_out_2");
+ clks[clk_out_2] = clk;
+
+ /* clk_out_3 */
+ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents,
+ ARRAY_SIZE(clk_out1_parents), 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0,
+ &clk_out_lock);
+ clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0,
+ pmc_base + PMC_CLK_OUT_CNTRL, 18, 0,
+ &clk_out_lock);
+ clk_register_clkdev(clk, "extern3", "clk_out_3");
+ clks[clk_out_3] = clk;
+
+ /* blink */
+ writel_relaxed(0, pmc_base + PMC_BLINK_TIMER);
+ clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0,
+ pmc_base + PMC_DPD_PADS_ORIDE,
+ PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL);
+ clk = clk_register_gate(NULL, "blink", "blink_override", 0,
+ pmc_base + PMC_CTRL,
+ PMC_CTRL_BLINK_ENB, 0, NULL);
+ clk_register_clkdev(clk, "blink", NULL);
+ clks[blink] = clk;
+
+}
+
+static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p_cclkg", "pll_p_out4_cclkg",
+ "pll_p_out3_cclkg", "unused", "pll_x" };
+static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p_cclklp", "pll_p_out4_cclklp",
+ "pll_p_out3_cclklp", "unused", "pll_x",
+ "pll_x_out0" };
+static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
+ "pll_p_out3", "pll_p_out2", "unused",
+ "clk_32k", "pll_m_out1" };
+
+static void __init tegra30_super_clk_init(void)
+{
+ struct clk *clk;
+
+ /*
+ * Clock input to cclk_g divided from pll_p using
+ * U71 divider of cclk_g.
+ */
+ clk = tegra_clk_register_divider("pll_p_cclkg", "pll_p",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_cclkg", NULL);
+
+ /*
+ * Clock input to cclk_g divided from pll_p_out3 using
+ * U71 divider of cclk_g.
+ */
+ clk = tegra_clk_register_divider("pll_p_out3_cclkg", "pll_p_out3",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out3_cclkg", NULL);
+
+ /*
+ * Clock input to cclk_g divided from pll_p_out4 using
+ * U71 divider of cclk_g.
+ */
+ clk = tegra_clk_register_divider("pll_p_out4_cclkg", "pll_p_out4",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL);
+
+ /* CCLKG */
+ clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents,
+ ARRAY_SIZE(cclk_g_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKG_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ clk_register_clkdev(clk, "cclk_g", NULL);
+ clks[cclk_g] = clk;
+
+ /*
+ * Clock input to cclk_lp divided from pll_p using
+ * U71 divider of cclk_lp.
+ */
+ clk = tegra_clk_register_divider("pll_p_cclklp", "pll_p",
+ clk_base + SUPER_CCLKLP_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_cclklp", NULL);
+
+ /*
+ * Clock input to cclk_lp divided from pll_p_out3 using
+ * U71 divider of cclk_lp.
+ */
+ clk = tegra_clk_register_divider("pll_p_out3_cclklp", "pll_p_out3",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out3_cclklp", NULL);
+
+ /*
+ * Clock input to cclk_lp divided from pll_p_out4 using
+ * U71 divider of cclk_lp.
+ */
+ clk = tegra_clk_register_divider("pll_p_out4_cclklp", "pll_p_out4",
+ clk_base + SUPER_CCLKLP_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out4_cclklp", NULL);
+
+ /* CCLKLP */
+ clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents,
+ ARRAY_SIZE(cclk_lp_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKLP_BURST_POLICY,
+ TEGRA_DIVIDER_2, 4, 8, 9,
+ NULL);
+ clk_register_clkdev(clk, "cclk_lp", NULL);
+ clks[cclk_lp] = clk;
+
+ /* SCLK */
+ clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+ ARRAY_SIZE(sclk_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + SCLK_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ clk_register_clkdev(clk, "sclk", NULL);
+ clks[sclk] = clk;
+
+ /* HCLK */
+ clk = clk_register_divider(NULL, "hclk_div", "sclk", 0,
+ clk_base + SYSTEM_CLK_RATE, 4, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT,
+ clk_base + SYSTEM_CLK_RATE, 7,
+ CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ clk_register_clkdev(clk, "hclk", NULL);
+ clks[hclk] = clk;
+
+ /* PCLK */
+ clk = clk_register_divider(NULL, "pclk_div", "hclk", 0,
+ clk_base + SYSTEM_CLK_RATE, 0, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT,
+ clk_base + SYSTEM_CLK_RATE, 3,
+ CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ clk_register_clkdev(clk, "pclk", NULL);
+ clks[pclk] = clk;
+
+ /* twd */
+ clk = clk_register_fixed_factor(NULL, "twd", "cclk_g",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "twd", NULL);
+ clks[twd] = clk;
+}
+
+static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p",
+ "clk_m" };
+static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
+static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
+static const char *i2s0_parents[] = { "pll_a_out0", "audio0_2x", "pll_p",
+ "clk_m" };
+static const char *i2s1_parents[] = { "pll_a_out0", "audio1_2x", "pll_p",
+ "clk_m" };
+static const char *i2s2_parents[] = { "pll_a_out0", "audio2_2x", "pll_p",
+ "clk_m" };
+static const char *i2s3_parents[] = { "pll_a_out0", "audio3_2x", "pll_p",
+ "clk_m" };
+static const char *i2s4_parents[] = { "pll_a_out0", "audio4_2x", "pll_p",
+ "clk_m" };
+static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p",
+ "clk_m" };
+static const char *spdif_in_parents[] = { "pll_p", "pll_c", "pll_m" };
+static const char *mux_pllpc_clk32k_clkm[] = { "pll_p", "pll_c", "clk_32k",
+ "clk_m" };
+static const char *mux_pllpc_clkm_clk32k[] = { "pll_p", "pll_c", "clk_m",
+ "clk_32k" };
+static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" };
+static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c",
+ "clk_m" };
+static const char *mux_pllp_clkm[] = { "pll_p", "unused", "unused", "clk_m" };
+static const char *mux_pllpmdacd2_clkm[] = { "pll_p", "pll_m", "pll_d_out0",
+ "pll_a_out0", "pll_c",
+ "pll_d2_out0", "clk_m" };
+static const char *mux_plla_clk32k_pllp_clkm_plle[] = { "pll_a_out0",
+ "clk_32k", "pll_p",
+ "clk_m", "pll_e" };
+static const char *mux_plld_out0_plld2_out0[] = { "pll_d_out0",
+ "pll_d2_out0" };
+
+static struct tegra_periph_init_data tegra_periph_clk_list[] = {
+ TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", i2s0_parents, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0),
+ TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", i2s1_parents, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1),
+ TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", i2s2_parents, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2),
+ TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", i2s3_parents, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3),
+ TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", i2s4_parents, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4),
+ TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", spdif_out_parents, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out),
+ TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", spdif_in_parents, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in),
+ TEGRA_INIT_DATA_MUX("d_audio", "d_audio", "tegra30-ahub", mux_pllacp_clkm, CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, 0, d_audio),
+ TEGRA_INIT_DATA_MUX("dam0", NULL, "tegra30-dam.0", mux_pllacp_clkm, CLK_SOURCE_DAM0, 108, &periph_v_regs, 0, dam0),
+ TEGRA_INIT_DATA_MUX("dam1", NULL, "tegra30-dam.1", mux_pllacp_clkm, CLK_SOURCE_DAM1, 109, &periph_v_regs, 0, dam1),
+ TEGRA_INIT_DATA_MUX("dam2", NULL, "tegra30-dam.2", mux_pllacp_clkm, CLK_SOURCE_DAM2, 110, &periph_v_regs, 0, dam2),
+ TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllpcm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, 0, hda),
+ TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllpcm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, 0, hda2codec_2x),
+ TEGRA_INIT_DATA_MUX("sbc1", NULL, "spi_tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1),
+ TEGRA_INIT_DATA_MUX("sbc2", NULL, "spi_tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2),
+ TEGRA_INIT_DATA_MUX("sbc3", NULL, "spi_tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3),
+ TEGRA_INIT_DATA_MUX("sbc4", NULL, "spi_tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4),
+ TEGRA_INIT_DATA_MUX("sbc5", NULL, "spi_tegra.4", mux_pllpcm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5),
+ TEGRA_INIT_DATA_MUX("sbc6", NULL, "spi_tegra.5", mux_pllpcm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6),
+ TEGRA_INIT_DATA_MUX("sata_oob", NULL, "tegra_sata_oob", mux_pllpcm_clkm, CLK_SOURCE_SATA_OOB, 123, &periph_v_regs, TEGRA_PERIPH_ON_APB, sata_oob),
+ TEGRA_INIT_DATA_MUX("sata", NULL, "tegra_sata", mux_pllpcm_clkm, CLK_SOURCE_SATA, 124, &periph_v_regs, TEGRA_PERIPH_ON_APB, sata),
+ TEGRA_INIT_DATA_MUX("ndflash", NULL, "tegra_nand", mux_pllpcm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_l_regs, TEGRA_PERIPH_ON_APB, ndflash),
+ TEGRA_INIT_DATA_MUX("ndspeed", NULL, "tegra_nand_speed", mux_pllpcm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed),
+ TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllpcm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir),
+ TEGRA_INIT_DATA_MUX("csite", NULL, "csite", mux_pllpcm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite),
+ TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllpcm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la),
+ TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllpcm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr),
+ TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllpcm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi),
+ TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllpc_clkm_clk32k, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor),
+ TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllpc_clk32k_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow),
+ TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllpcm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde),
+ TEGRA_INIT_DATA_INT("vi", "vi", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi),
+ TEGRA_INIT_DATA_INT("epp", NULL, "epp", mux_pllmcpa, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp),
+ TEGRA_INIT_DATA_INT("mpe", NULL, "mpe", mux_pllmcpa, CLK_SOURCE_MPE, 60, &periph_h_regs, 0, mpe),
+ TEGRA_INIT_DATA_INT("host1x", NULL, "host1x", mux_pllmcpa, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x),
+ TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllmcpa, CLK_SOURCE_3D, 24, &periph_l_regs, TEGRA_PERIPH_MANUAL_RESET, gr3d),
+ TEGRA_INIT_DATA_INT("3d2", NULL, "3d2", mux_pllmcpa, CLK_SOURCE_3D2, 98, &periph_v_regs, TEGRA_PERIPH_MANUAL_RESET, gr3d2),
+ TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllmcpa, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr2d),
+ TEGRA_INIT_DATA_INT("se", NULL, "se", mux_pllpcm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, 0, se),
+ TEGRA_INIT_DATA_MUX("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect),
+ TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllpcm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor),
+ TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1),
+ TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2),
+ TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3),
+ TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4),
+ TEGRA_INIT_DATA_MUX("cve", NULL, "cve", mux_pllpdc_clkm, CLK_SOURCE_CVE, 49, &periph_h_regs, 0, cve),
+ TEGRA_INIT_DATA_MUX("tvo", NULL, "tvo", mux_pllpdc_clkm, CLK_SOURCE_TVO, 49, &periph_h_regs, 0, tvo),
+ TEGRA_INIT_DATA_MUX("tvdac", NULL, "tvdac", mux_pllpdc_clkm, CLK_SOURCE_TVDAC, 53, &periph_h_regs, 0, tvdac),
+ TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllpc_clk32k_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon),
+ TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor),
+ TEGRA_INIT_DATA_DIV16("i2c1", "div-clk", "tegra-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2c1),
+ TEGRA_INIT_DATA_DIV16("i2c2", "div-clk", "tegra-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, TEGRA_PERIPH_ON_APB, i2c2),
+ TEGRA_INIT_DATA_DIV16("i2c3", "div-clk", "tegra-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2c3),
+ TEGRA_INIT_DATA_DIV16("i2c4", "div-clk", "tegra-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2c4),
+ TEGRA_INIT_DATA_DIV16("i2c5", "div-clk", "tegra-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, TEGRA_PERIPH_ON_APB, i2c5),
+ TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllpcm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta),
+ TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllpcm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb),
+ TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllpcm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc),
+ TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllpcm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd),
+ TEGRA_INIT_DATA_UART("uarte", NULL, "tegra_uart.4", mux_pllpcm_clkm, CLK_SOURCE_UARTE, 66, &periph_u_regs, uarte),
+ TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllpmdacd2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi),
+ TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32k_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1),
+ TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32k_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2),
+ TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32k_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3),
+ TEGRA_INIT_DATA("pwm", NULL, "pwm", mux_pllpc_clk32k_clkm, CLK_SOURCE_PWM, 28, 2, 0, 0, 8, 1, 0, &periph_l_regs, 17, periph_clk_enb_refcnt, 0, pwm),
+};
+
+static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
+ TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllpmdacd2_clkm, CLK_SOURCE_DISP1, 29, 3, 27, &periph_l_regs, 0, disp1),
+ TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllpmdacd2_clkm, CLK_SOURCE_DISP2, 29, 3, 26, &periph_l_regs, 0, disp2),
+ TEGRA_INIT_DATA_NODIV("dsib", NULL, "tegradc.1", mux_plld_out0_plld2_out0, CLK_SOURCE_DSIB, 25, 1, 82, &periph_u_regs, 0, dsib),
+};
+
+static void __init tegra30_periph_clk_init(void)
+{
+ struct tegra_periph_init_data *data;
+ struct clk *clk;
+ int i;
+
+ /* apbdma */
+ clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base, 0, 34,
+ &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-apbdma");
+ clks[apbdma] = clk;
+
+ /* rtc */
+ clk = tegra_clk_register_periph_gate("rtc", "clk_32k",
+ TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 4, &periph_l_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "rtc-tegra");
+ clks[rtc] = clk;
+
+ /* timer */
+ clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, 0,
+ 5, &periph_l_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "timer");
+ clks[timer] = clk;
+
+ /* kbc */
+ clk = tegra_clk_register_periph_gate("kbc", "clk_32k",
+ TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 36, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-kbc");
+ clks[kbc] = clk;
+
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "clk_m",
+ TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 92, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "csus", "tengra_camera");
+ clks[csus] = clk;
+
+ /* vcp */
+ clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base, 0, 29,
+ &periph_l_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "vcp", "tegra-avp");
+ clks[vcp] = clk;
+
+ /* bsea */
+ clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base, 0,
+ 62, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "bsea", "tegra-avp");
+ clks[bsea] = clk;
+
+ /* bsev */
+ clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base, 0,
+ 63, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "bsev", "tegra-aes");
+ clks[bsev] = clk;
+
+ /* usbd */
+ clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, 0,
+ 22, &periph_l_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "fsl-tegra-udc");
+ clks[usbd] = clk;
+
+ /* usb2 */
+ clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, 0,
+ 58, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-ehci.1");
+ clks[usb2] = clk;
+
+ /* usb3 */
+ clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, 0,
+ 59, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra-ehci.2");
+ clks[usb3] = clk;
+
+ /* dsia */
+ clk = tegra_clk_register_periph_gate("dsia", "pll_d_out0", 0, clk_base,
+ 0, 48, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "dsia", "tegradc.0");
+ clks[dsia] = clk;
+
+ /* csi */
+ clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base,
+ 0, 52, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "csi", "tegra_camera");
+ clks[csi] = clk;
+
+ /* isp */
+ clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, 23,
+ &periph_l_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "isp", "tegra_camera");
+ clks[isp] = clk;
+
+ /* pcie */
+ clk = tegra_clk_register_periph_gate("pcie", "clk_m", 0, clk_base, 0,
+ 70, &periph_u_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "pcie", "tegra-pcie");
+ clks[pcie] = clk;
+
+ /* afi */
+ clk = tegra_clk_register_periph_gate("afi", "clk_m", 0, clk_base, 0, 72,
+ &periph_u_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "afi", "tegra-pcie");
+ clks[afi] = clk;
+
+ /* pciex */
+ clk = tegra_clk_register_periph_gate("pciex", "pll_e", 0, clk_base, 0,
+ 74, &periph_u_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "pciex", "tegra-pcie");
+ clks[pciex] = clk;
+
+ /* kfuse */
+ clk = tegra_clk_register_periph_gate("kfuse", "clk_m",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 40, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "kfuse-tegra");
+ clks[kfuse] = clk;
+
+ /* fuse */
+ clk = tegra_clk_register_periph_gate("fuse", "clk_m",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 39, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "fuse", "fuse-tegra");
+ clks[fuse] = clk;
+
+ /* fuse_burn */
+ clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 39, &periph_h_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "fuse_burn", "fuse-tegra");
+ clks[fuse_burn] = clk;
+
+ /* apbif */
+ clk = tegra_clk_register_periph_gate("apbif", "clk_m", 0,
+ clk_base, 0, 107, &periph_v_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "apbif", "tegra30-ahub");
+ clks[apbif] = clk;
+
+ /* hda2hdmi */
+ clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 128, &periph_w_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "hda2hdmi", "tegra30-hda");
+ clks[hda2hdmi] = clk;
+
+ /* sata_cold */
+ clk = tegra_clk_register_periph_gate("sata_cold", "clk_m",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 129, &periph_w_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "tegra_sata_cold");
+ clks[sata_cold] = clk;
+
+ /* dtv */
+ clk = tegra_clk_register_periph_gate("dtv", "clk_m",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 79, &periph_u_regs,
+ periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, NULL, "dtv");
+ clks[dtv] = clk;
+
+ /* emc */
+ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+ ARRAY_SIZE(mux_pllmcp_clkm), 0,
+ clk_base + CLK_SOURCE_EMC,
+ 30, 2, 0, NULL);
+ clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
+ 57, &periph_h_regs, periph_clk_enb_refcnt);
+ clk_register_clkdev(clk, "emc", NULL);
+ clks[emc] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
+ data = &tegra_periph_clk_list[i];
+ clk = tegra_clk_register_periph(data->name, data->parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset, data->flags);
+ clk_register_clkdev(clk, data->con_id, data->dev_id);
+ clks[data->clk_id] = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) {
+ data = &tegra_periph_nodiv_clk_list[i];
+ clk = tegra_clk_register_periph_nodiv(data->name,
+ data->parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset);
+ clk_register_clkdev(clk, data->con_id, data->dev_id);
+ clks[data->clk_id] = clk;
+ }
+}
+
+static void __init tegra30_fixed_clk_init(void)
+{
+ struct clk *clk;
+
+ /* clk_32k */
+ clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT,
+ 32768);
+ clk_register_clkdev(clk, "clk_32k", NULL);
+ clks[clk_32k] = clk;
+
+ /* clk_m_div2 */
+ clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "clk_m_div2", NULL);
+ clks[clk_m_div2] = clk;
+
+ /* clk_m_div4 */
+ clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m",
+ CLK_SET_RATE_PARENT, 1, 4);
+ clk_register_clkdev(clk, "clk_m_div4", NULL);
+ clks[clk_m_div4] = clk;
+
+ /* cml0 */
+ clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
+ 0, 0, &cml_lock);
+ clk_register_clkdev(clk, "cml0", NULL);
+ clks[cml0] = clk;
+
+ /* cml1 */
+ clk = clk_register_gate(NULL, "cml1", "pll_e", 0, clk_base + PLLE_AUX,
+ 1, 0, &cml_lock);
+ clk_register_clkdev(clk, "cml1", NULL);
+ clks[cml1] = clk;
+}
+
+static void __init tegra30_osc_clk_init(void)
+{
+ struct clk *clk;
+ unsigned int pll_ref_div;
+
+ tegra30_clk_measure_input_freq();
+
+ /* clk_m */
+ clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT,
+ input_freq);
+ clk_register_clkdev(clk, "clk_m", NULL);
+ clks[clk_m] = clk;
+
+ /* pll_ref */
+ pll_ref_div = tegra30_get_pll_ref_div();
+ clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m",
+ CLK_SET_RATE_PARENT, 1, pll_ref_div);
+ clk_register_clkdev(clk, "pll_ref", NULL);
+ clks[pll_ref] = clk;
+}
+
+/* Tegra30 CPU clock and reset control functions */
+static void tegra30_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+
+ return;
+}
+
+static void tegra30_put_cpu_in_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ dmb();
+}
+
+static void tegra30_cpu_out_of_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+ wmb();
+}
+
+
+static void tegra30_enable_cpu_clock(u32 cpu)
+{
+ unsigned int reg;
+
+ writel(CPU_CLOCK(cpu),
+ clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ reg = readl(clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+}
+
+static void tegra30_disable_cpu_clock(u32 cpu)
+{
+
+ unsigned int reg;
+
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg | CPU_CLOCK(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static bool tegra30_cpu_rail_off_ready(void)
+{
+ unsigned int cpu_rst_status;
+ int cpu_pwr_status;
+
+ cpu_rst_status = readl(clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_pwr_status = tegra_powergate_is_powered(TEGRA_POWERGATE_CPU1) ||
+ tegra_powergate_is_powered(TEGRA_POWERGATE_CPU2) ||
+ tegra_powergate_is_powered(TEGRA_POWERGATE_CPU3);
+
+ if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status)
+ return false;
+
+ return true;
+}
+
+static void tegra30_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra30_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_RESET_SOURCE_CSITE);
+ writel(3<<30, clk_base + CLK_RESET_SOURCE_CSITE);
+
+ tegra30_cpu_clk_sctx.cpu_burst =
+ readl(clk_base + CLK_RESET_CCLK_BURST);
+ tegra30_cpu_clk_sctx.pllx_base =
+ readl(clk_base + CLK_RESET_PLLX_BASE);
+ tegra30_cpu_clk_sctx.pllx_misc =
+ readl(clk_base + CLK_RESET_PLLX_MISC);
+ tegra30_cpu_clk_sctx.cclk_divider =
+ readl(clk_base + CLK_RESET_CCLK_DIVIDER);
+}
+
+static void tegra30_cpu_clock_resume(void)
+{
+ unsigned int reg, policy;
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(clk_base + CLK_RESET_CCLK_BURST);
+ policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+ if (policy == CLK_RESET_CCLK_IDLE_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF;
+ else if (policy == CLK_RESET_CCLK_RUN_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF;
+ else
+ BUG();
+
+ if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) {
+ /* restore PLLX settings if CPU is on different PLL */
+ writel(tegra30_cpu_clk_sctx.pllx_misc,
+ clk_base + CLK_RESET_PLLX_MISC);
+ writel(tegra30_cpu_clk_sctx.pllx_base,
+ clk_base + CLK_RESET_PLLX_BASE);
+
+ /* wait for PLL stabilization if PLLX was enabled */
+ if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30))
+ udelay(300);
+ }
+
+ /*
+ * Restore original burst policy setting for calls resulting from CPU
+ * LP2 in idle or system suspend.
+ */
+ writel(tegra30_cpu_clk_sctx.cclk_divider,
+ clk_base + CLK_RESET_CCLK_DIVIDER);
+ writel(tegra30_cpu_clk_sctx.cpu_burst,
+ clk_base + CLK_RESET_CCLK_BURST);
+
+ writel(tegra30_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_RESET_SOURCE_CSITE);
+}
+#endif
+
+static struct tegra_cpu_car_ops tegra30_cpu_car_ops = {
+ .wait_for_reset = tegra30_wait_cpu_in_reset,
+ .put_in_reset = tegra30_put_cpu_in_reset,
+ .out_of_reset = tegra30_cpu_out_of_reset,
+ .enable_clock = tegra30_enable_cpu_clock,
+ .disable_clock = tegra30_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .rail_off_ready = tegra30_cpu_rail_off_ready,
+ .suspend = tegra30_cpu_clock_suspend,
+ .resume = tegra30_cpu_clock_resume,
+#endif
+};
+
+static __initdata struct tegra_clk_init_table init_table[] = {
+ {uarta, pll_p, 408000000, 0},
+ {uartb, pll_p, 408000000, 0},
+ {uartc, pll_p, 408000000, 0},
+ {uartd, pll_p, 408000000, 0},
+ {uarte, pll_p, 408000000, 0},
+ {pll_a, clk_max, 564480000, 1},
+ {pll_a_out0, clk_max, 11289600, 1},
+ {extern1, pll_a_out0, 0, 1},
+ {clk_out_1_mux, extern1, 0, 0},
+ {clk_out_1, clk_max, 0, 1},
+ {blink, clk_max, 0, 1},
+ {i2s0, pll_a_out0, 11289600, 0},
+ {i2s1, pll_a_out0, 11289600, 0},
+ {i2s2, pll_a_out0, 11289600, 0},
+ {i2s3, pll_a_out0, 11289600, 0},
+ {i2s4, pll_a_out0, 11289600, 0},
+ {sdmmc1, pll_p, 48000000, 0},
+ {sdmmc2, pll_p, 48000000, 0},
+ {sdmmc3, pll_p, 48000000, 0},
+ {pll_m, clk_max, 0, 1},
+ {pclk, clk_max, 0, 1},
+ {csite, clk_max, 0, 1},
+ {emc, clk_max, 0, 1},
+ {mselect, clk_max, 0, 1},
+ {sbc1, pll_p, 100000000, 0},
+ {sbc2, pll_p, 100000000, 0},
+ {sbc3, pll_p, 100000000, 0},
+ {sbc4, pll_p, 100000000, 0},
+ {sbc5, pll_p, 100000000, 0},
+ {sbc6, pll_p, 100000000, 0},
+ {host1x, pll_c, 150000000, 0},
+ {disp1, pll_p, 600000000, 0},
+ {disp2, pll_p, 600000000, 0},
+ {twd, clk_max, 0, 1},
+ {gr2d, pll_c, 300000000, 0},
+ {gr3d, pll_c, 300000000, 0},
+ {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */
+};
+
+static void __init tegra30_clock_apply_init_table(void)
+{
+ tegra_init_from_table(init_table, clks, clk_max);
+}
+
+/*
+ * Some clocks may be used by different drivers depending on the board
+ * configuration. List those here to register them twice in the clock lookup
+ * table under two names.
+ */
+static struct tegra_clk_duplicate tegra_clk_duplicates[] = {
+ TEGRA_CLK_DUPLICATE(usbd, "utmip-pad", NULL),
+ TEGRA_CLK_DUPLICATE(usbd, "tegra-ehci.0", NULL),
+ TEGRA_CLK_DUPLICATE(usbd, "tegra-otg", NULL),
+ TEGRA_CLK_DUPLICATE(bsev, "tegra-avp", "bsev"),
+ TEGRA_CLK_DUPLICATE(bsev, "nvavp", "bsev"),
+ TEGRA_CLK_DUPLICATE(vde, "tegra-aes", "vde"),
+ TEGRA_CLK_DUPLICATE(bsea, "tegra-aes", "bsea"),
+ TEGRA_CLK_DUPLICATE(bsea, "nvavp", "bsea"),
+ TEGRA_CLK_DUPLICATE(cml1, "tegra_sata_cml", NULL),
+ TEGRA_CLK_DUPLICATE(cml0, "tegra_pcie", "cml"),
+ TEGRA_CLK_DUPLICATE(pciex, "tegra_pcie", "pciex"),
+ TEGRA_CLK_DUPLICATE(vcp, "nvavp", "vcp"),
+ TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* MUST be the last entry */
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra30-pmc" },
+ {},
+};
+
+void __init tegra30_clock_init(struct device_node *np)
+{
+ struct device_node *node;
+ int i;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("ioremap tegra30 CAR failed\n");
+ return;
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ BUG();
+ }
+
+ pmc_base = of_iomap(node, 0);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ BUG();
+ }
+
+ tegra30_osc_clk_init();
+ tegra30_fixed_clk_init();
+ tegra30_pll_init();
+ tegra30_super_clk_init();
+ tegra30_periph_clk_init();
+ tegra30_audio_clk_init();
+ tegra30_pmc_clk_init();
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+ if (IS_ERR(clks[i])) {
+ pr_err("Tegra30 clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ BUG();
+ }
+ if (!clks[i])
+ clks[i] = ERR_PTR(-EINVAL);
+ }
+
+ tegra_init_dup_clks(tegra_clk_duplicates, clks, clk_max);
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
+
+ tegra_cpu_car_ops = &tegra30_cpu_car_ops;
+}
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
new file mode 100644
index 000000000..923ca7ee4
--- /dev/null
+++ b/drivers/clk/tegra/clk.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/clk/tegra.h>
+
+#include "clk.h"
+
+/* Global data of Tegra CPU CAR ops */
+static struct tegra_cpu_car_ops dummy_car_ops;
+struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
+
+void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
+ struct clk *clks[], int clk_max)
+{
+ struct clk *clk;
+
+ for (; dup_list->clk_id < clk_max; dup_list++) {
+ clk = clks[dup_list->clk_id];
+ dup_list->lookup.clk = clk;
+ clkdev_add(&dup_list->lookup);
+ }
+}
+
+void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
+ struct clk *clks[], int clk_max)
+{
+ struct clk *clk;
+
+ for (; tbl->clk_id < clk_max; tbl++) {
+ clk = clks[tbl->clk_id];
+ if (IS_ERR_OR_NULL(clk))
+ return;
+
+ if (tbl->parent_id < clk_max) {
+ struct clk *parent = clks[tbl->parent_id];
+ if (clk_set_parent(clk, parent)) {
+ pr_err("%s: Failed to set parent %s of %s\n",
+ __func__, __clk_get_name(parent),
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+ }
+
+ if (tbl->rate)
+ if (clk_set_rate(clk, tbl->rate)) {
+ pr_err("%s: Failed to set rate %lu of %s\n",
+ __func__, tbl->rate,
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+
+ if (tbl->state)
+ if (clk_prepare_enable(clk)) {
+ pr_err("%s: Failed to enable %s\n", __func__,
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+ }
+}
+
+static const struct of_device_id tegra_dt_clk_match[] = {
+ { .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init },
+ { .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init },
+ { .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init },
+ { }
+};
+
+void __init tegra_clocks_init(void)
+{
+ of_clk_init(tegra_dt_clk_match);
+}
+
+tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
+
+void __init tegra_clocks_apply_init_table(void)
+{
+ if (!tegra_clk_apply_init_table)
+ return;
+
+ tegra_clk_apply_init_table();
+}
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
new file mode 100644
index 000000000..e0565620d
--- /dev/null
+++ b/drivers/clk/tegra/clk.h
@@ -0,0 +1,595 @@
+ /*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA_CLK_H
+#define __TEGRA_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+/**
+ * struct tegra_clk_sync_source - external clock source from codec
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @rate: input frequency from source
+ * @max_rate: max rate allowed
+ */
+struct tegra_clk_sync_source {
+ struct clk_hw hw;
+ unsigned long rate;
+ unsigned long max_rate;
+};
+
+#define to_clk_sync_source(_hw) \
+ container_of(_hw, struct tegra_clk_sync_source, hw)
+
+extern const struct clk_ops tegra_clk_sync_source_ops;
+struct clk *tegra_clk_register_sync_source(const char *name,
+ unsigned long fixed_rate, unsigned long max_rate);
+
+/**
+ * struct tegra_clk_frac_div - fractional divider clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register containing divider
+ * @flags: hardware-specific flags
+ * @shift: shift to the divider bit field
+ * @width: width of the divider bit field
+ * @frac_width: width of the fractional bit field
+ * @lock: register lock
+ *
+ * Flags:
+ * TEGRA_DIVIDER_ROUND_UP - This flags indicates to round up the divider value.
+ * TEGRA_DIVIDER_FIXED - Fixed rate PLL dividers has addition override bit, this
+ * flag indicates that this divider is for fixed rate PLL.
+ * TEGRA_DIVIDER_INT - Some modules can not cope with the duty cycle when
+ * fraction bit is set. This flags indicates to calculate divider for which
+ * fracton bit will be zero.
+ * TEGRA_DIVIDER_UART - UART module divider has additional enable bit which is
+ * set when divider value is not 0. This flags indicates that the divider
+ * is for UART module.
+ */
+struct tegra_clk_frac_div {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 flags;
+ u8 shift;
+ u8 width;
+ u8 frac_width;
+ spinlock_t *lock;
+};
+
+#define to_clk_frac_div(_hw) container_of(_hw, struct tegra_clk_frac_div, hw)
+
+#define TEGRA_DIVIDER_ROUND_UP BIT(0)
+#define TEGRA_DIVIDER_FIXED BIT(1)
+#define TEGRA_DIVIDER_INT BIT(2)
+#define TEGRA_DIVIDER_UART BIT(3)
+
+extern const struct clk_ops tegra_clk_frac_div_ops;
+struct clk *tegra_clk_register_divider(const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
+ u8 frac_width, spinlock_t *lock);
+
+/*
+ * Tegra PLL:
+ *
+ * In general, there are 3 requirements for each PLL
+ * that SW needs to be comply with.
+ * (1) Input frequency range (REF).
+ * (2) Comparison frequency range (CF). CF = REF/DIVM.
+ * (3) VCO frequency range (VCO). VCO = CF * DIVN.
+ *
+ * The final PLL output frequency (FO) = VCO >> DIVP.
+ */
+
+/**
+ * struct tegra_clk_pll_freq_table - PLL frequecy table
+ *
+ * @input_rate: input rate from source
+ * @output_rate: output rate from PLL for the input rate
+ * @n: feedback divider
+ * @m: input divider
+ * @p: post divider
+ * @cpcon: charge pump current
+ */
+struct tegra_clk_pll_freq_table {
+ unsigned long input_rate;
+ unsigned long output_rate;
+ u16 n;
+ u16 m;
+ u8 p;
+ u8 cpcon;
+};
+
+/**
+ * struct pdiv_map - map post divider to hw value
+ *
+ * @pdiv: post divider
+ * @hw_val: value to be written to the PLL hw
+ */
+struct pdiv_map {
+ u8 pdiv;
+ u8 hw_val;
+};
+
+/**
+ * struct clk_pll_params - PLL parameters
+ *
+ * @input_min: Minimum input frequency
+ * @input_max: Maximum input frequency
+ * @cf_min: Minimum comparison frequency
+ * @cf_max: Maximum comparison frequency
+ * @vco_min: Minimum VCO frequency
+ * @vco_max: Maximum VCO frequency
+ * @base_reg: PLL base reg offset
+ * @misc_reg: PLL misc reg offset
+ * @lock_reg: PLL lock reg offset
+ * @lock_bit_idx: Bit index for PLL lock status
+ * @lock_enable_bit_idx: Bit index to enable PLL lock
+ * @lock_delay: Delay in us if PLL lock is not used
+ */
+struct tegra_clk_pll_params {
+ unsigned long input_min;
+ unsigned long input_max;
+ unsigned long cf_min;
+ unsigned long cf_max;
+ unsigned long vco_min;
+ unsigned long vco_max;
+
+ u32 base_reg;
+ u32 misc_reg;
+ u32 lock_reg;
+ u32 lock_mask;
+ u32 lock_enable_bit_idx;
+ u32 iddq_reg;
+ u32 iddq_bit_idx;
+ u32 aux_reg;
+ u32 dyn_ramp_reg;
+ u32 ext_misc_reg[3];
+ int stepa_shift;
+ int stepb_shift;
+ int lock_delay;
+ int max_p;
+ struct pdiv_map *pdiv_tohw;
+};
+
+/**
+ * struct tegra_clk_pll - Tegra PLL clock
+ *
+ * @hw: handle between common and hardware-specifix interfaces
+ * @clk_base: address of CAR controller
+ * @pmc: address of PMC, required to read override bits
+ * @freq_table: array of frequencies supported by PLL
+ * @params: PLL parameters
+ * @flags: PLL flags
+ * @fixed_rate: PLL rate if it is fixed
+ * @lock: register lock
+ * @divn_shift: shift to the feedback divider bit field
+ * @divn_width: width of the feedback divider bit field
+ * @divm_shift: shift to the input divider bit field
+ * @divm_width: width of the input divider bit field
+ * @divp_shift: shift to the post divider bit field
+ * @divp_width: width of the post divider bit field
+ *
+ * Flags:
+ * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for
+ * PLL locking. If not set it will use lock_delay value to wait.
+ * TEGRA_PLL_HAS_CPCON - This flag indicates that CPCON value needs
+ * to be programmed to change output frequency of the PLL.
+ * TEGRA_PLL_SET_LFCON - This flag indicates that LFCON value needs
+ * to be programmed to change output frequency of the PLL.
+ * TEGRA_PLL_SET_DCCON - This flag indicates that DCCON value needs
+ * to be programmed to change output frequency of the PLL.
+ * TEGRA_PLLU - PLLU has inverted post divider. This flags indicated
+ * that it is PLLU and invert post divider value.
+ * TEGRA_PLLM - PLLM has additional override settings in PMC. This
+ * flag indicates that it is PLLM and use override settings.
+ * TEGRA_PLL_FIXED - We are not supposed to change output frequency
+ * of some plls.
+ * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling.
+ * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the
+ * base register.
+ * TEGRA_PLL_BYPASS - PLL has bypass bit
+ * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring
+ */
+struct tegra_clk_pll {
+ struct clk_hw hw;
+ void __iomem *clk_base;
+ void __iomem *pmc;
+ u32 flags;
+ unsigned long fixed_rate;
+ spinlock_t *lock;
+ u8 divn_shift;
+ u8 divn_width;
+ u8 divm_shift;
+ u8 divm_width;
+ u8 divp_shift;
+ u8 divp_width;
+ struct tegra_clk_pll_freq_table *freq_table;
+ struct tegra_clk_pll_params *params;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw)
+
+#define TEGRA_PLL_USE_LOCK BIT(0)
+#define TEGRA_PLL_HAS_CPCON BIT(1)
+#define TEGRA_PLL_SET_LFCON BIT(2)
+#define TEGRA_PLL_SET_DCCON BIT(3)
+#define TEGRA_PLLU BIT(4)
+#define TEGRA_PLLM BIT(5)
+#define TEGRA_PLL_FIXED BIT(6)
+#define TEGRA_PLLE_CONFIGURE BIT(7)
+#define TEGRA_PLL_LOCK_MISC BIT(8)
+#define TEGRA_PLL_BYPASS BIT(9)
+#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10)
+
+extern const struct clk_ops tegra_clk_pll_ops;
+extern const struct clk_ops tegra_clk_plle_ops;
+struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock);
+
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params, u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ u32 pll_flags,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock, unsigned long parent_rate);
+
+struct clk *tegra_clk_register_plle_tegra114(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ unsigned long fixed_rate,
+ struct tegra_clk_pll_params *pll_params,
+ struct tegra_clk_pll_freq_table *freq_table,
+ spinlock_t *lock);
+
+/**
+ * struct tegra_clk_pll_out - PLL divider down clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register containing the PLL divider
+ * @enb_bit_idx: bit to enable/disable PLL divider
+ * @rst_bit_idx: bit to reset PLL divider
+ * @lock: register lock
+ * @flags: hardware-specific flags
+ */
+struct tegra_clk_pll_out {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 enb_bit_idx;
+ u8 rst_bit_idx;
+ spinlock_t *lock;
+ u8 flags;
+};
+
+#define to_clk_pll_out(_hw) container_of(_hw, struct tegra_clk_pll_out, hw)
+
+extern const struct clk_ops tegra_clk_pll_out_ops;
+struct clk *tegra_clk_register_pll_out(const char *name,
+ const char *parent_name, void __iomem *reg, u8 enb_bit_idx,
+ u8 rst_bit_idx, unsigned long flags, u8 pll_div_flags,
+ spinlock_t *lock);
+
+/**
+ * struct tegra_clk_periph_regs - Registers controlling peripheral clock
+ *
+ * @enb_reg: read the enable status
+ * @enb_set_reg: write 1 to enable clock
+ * @enb_clr_reg: write 1 to disable clock
+ * @rst_reg: read the reset status
+ * @rst_set_reg: write 1 to assert the reset of peripheral
+ * @rst_clr_reg: write 1 to deassert the reset of peripheral
+ */
+struct tegra_clk_periph_regs {
+ u32 enb_reg;
+ u32 enb_set_reg;
+ u32 enb_clr_reg;
+ u32 rst_reg;
+ u32 rst_set_reg;
+ u32 rst_clr_reg;
+};
+
+/**
+ * struct tegra_clk_periph_gate - peripheral gate clock
+ *
+ * @magic: magic number to validate type
+ * @hw: handle between common and hardware-specific interfaces
+ * @clk_base: address of CAR controller
+ * @regs: Registers to control the peripheral
+ * @flags: hardware-specific flags
+ * @clk_num: Clock number
+ * @enable_refcnt: array to maintain reference count of the clock
+ *
+ * Flags:
+ * TEGRA_PERIPH_NO_RESET - This flag indicates that reset is not allowed
+ * for this module.
+ * TEGRA_PERIPH_MANUAL_RESET - This flag indicates not to reset module
+ * after clock enable and driver for the module is responsible for
+ * doing reset.
+ * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the
+ * bus to flush the write operation in apb bus. This flag indicates
+ * that this peripheral is in apb bus.
+ * TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug
+ */
+struct tegra_clk_periph_gate {
+ u32 magic;
+ struct clk_hw hw;
+ void __iomem *clk_base;
+ u8 flags;
+ int clk_num;
+ int *enable_refcnt;
+ struct tegra_clk_periph_regs *regs;
+};
+
+#define to_clk_periph_gate(_hw) \
+ container_of(_hw, struct tegra_clk_periph_gate, hw)
+
+#define TEGRA_CLK_PERIPH_GATE_MAGIC 0x17760309
+
+#define TEGRA_PERIPH_NO_RESET BIT(0)
+#define TEGRA_PERIPH_MANUAL_RESET BIT(1)
+#define TEGRA_PERIPH_ON_APB BIT(2)
+#define TEGRA_PERIPH_WAR_1005168 BIT(3)
+
+void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert);
+extern const struct clk_ops tegra_clk_periph_gate_ops;
+struct clk *tegra_clk_register_periph_gate(const char *name,
+ const char *parent_name, u8 gate_flags, void __iomem *clk_base,
+ unsigned long flags, int clk_num,
+ struct tegra_clk_periph_regs *pregs, int *enable_refcnt);
+
+/**
+ * struct clk-periph - peripheral clock
+ *
+ * @magic: magic number to validate type
+ * @hw: handle between common and hardware-specific interfaces
+ * @mux: mux clock
+ * @divider: divider clock
+ * @gate: gate clock
+ * @mux_ops: mux clock ops
+ * @div_ops: divider clock ops
+ * @gate_ops: gate clock ops
+ */
+struct tegra_clk_periph {
+ u32 magic;
+ struct clk_hw hw;
+ struct clk_mux mux;
+ struct tegra_clk_frac_div divider;
+ struct tegra_clk_periph_gate gate;
+
+ const struct clk_ops *mux_ops;
+ const struct clk_ops *div_ops;
+ const struct clk_ops *gate_ops;
+};
+
+#define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw)
+
+#define TEGRA_CLK_PERIPH_MAGIC 0x18221223
+
+extern const struct clk_ops tegra_clk_periph_ops;
+struct clk *tegra_clk_register_periph(const char *name,
+ const char **parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset, unsigned long flags);
+struct clk *tegra_clk_register_periph_nodiv(const char *name,
+ const char **parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset);
+
+#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \
+ _div_shift, _div_width, _div_frac_width, \
+ _div_flags, _clk_num, _enb_refcnt, _regs, \
+ _gate_flags, _table) \
+ { \
+ .mux = { \
+ .flags = _mux_flags, \
+ .shift = _mux_shift, \
+ .mask = _mux_mask, \
+ .table = _table, \
+ }, \
+ .divider = { \
+ .flags = _div_flags, \
+ .shift = _div_shift, \
+ .width = _div_width, \
+ .frac_width = _div_frac_width, \
+ }, \
+ .gate = { \
+ .flags = _gate_flags, \
+ .clk_num = _clk_num, \
+ .enable_refcnt = _enb_refcnt, \
+ .regs = _regs, \
+ }, \
+ .mux_ops = &clk_mux_ops, \
+ .div_ops = &tegra_clk_frac_div_ops, \
+ .gate_ops = &tegra_clk_periph_gate_ops, \
+ }
+
+struct tegra_periph_init_data {
+ const char *name;
+ int clk_id;
+ const char **parent_names;
+ int num_parents;
+ struct tegra_clk_periph periph;
+ u32 offset;
+ const char *con_id;
+ const char *dev_id;
+ unsigned long flags;
+};
+
+#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
+ _mux_shift, _mux_mask, _mux_flags, _div_shift, \
+ _div_width, _div_frac_width, _div_flags, _regs, \
+ _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\
+ _flags) \
+ { \
+ .name = _name, \
+ .clk_id = _clk_id, \
+ .parent_names = _parent_names, \
+ .num_parents = ARRAY_SIZE(_parent_names), \
+ .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \
+ _mux_flags, _div_shift, \
+ _div_width, _div_frac_width, \
+ _div_flags, _clk_num, \
+ _enb_refcnt, _regs, \
+ _gate_flags, _table), \
+ .offset = _offset, \
+ .con_id = _con_id, \
+ .dev_id = _dev_id, \
+ .flags = _flags \
+ }
+
+#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\
+ _mux_shift, _mux_width, _mux_flags, _div_shift, \
+ _div_width, _div_frac_width, _div_flags, _regs, \
+ _clk_num, _enb_refcnt, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
+ _mux_shift, BIT(_mux_width) - 1, _mux_flags, \
+ _div_shift, _div_width, _div_frac_width, _div_flags, \
+ _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\
+ NULL, 0)
+
+/**
+ * struct clk_super_mux - super clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register controlling multiplexer
+ * @width: width of the multiplexer bit field
+ * @flags: hardware-specific flags
+ * @div2_index: bit controlling divide-by-2
+ * @pllx_index: PLLX index in the parent list
+ * @lock: register lock
+ *
+ * Flags:
+ * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
+ * that this is LP cluster clock.
+ */
+struct tegra_clk_super_mux {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 width;
+ u8 flags;
+ u8 div2_index;
+ u8 pllx_index;
+ spinlock_t *lock;
+};
+
+#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
+
+#define TEGRA_DIVIDER_2 BIT(0)
+
+extern const struct clk_ops tegra_clk_super_ops;
+struct clk *tegra_clk_register_super_mux(const char *name,
+ const char **parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock);
+
+/**
+ * struct clk_init_tabel - clock initialization table
+ * @clk_id: clock id as mentioned in device tree bindings
+ * @parent_id: parent clock id as mentioned in device tree bindings
+ * @rate: rate to set
+ * @state: enable/disable
+ */
+struct tegra_clk_init_table {
+ unsigned int clk_id;
+ unsigned int parent_id;
+ unsigned long rate;
+ int state;
+};
+
+/**
+ * struct clk_duplicate - duplicate clocks
+ * @clk_id: clock id as mentioned in device tree bindings
+ * @lookup: duplicate lookup entry for the clock
+ */
+struct tegra_clk_duplicate {
+ int clk_id;
+ struct clk_lookup lookup;
+};
+
+#define TEGRA_CLK_DUPLICATE(_clk_id, _dev, _con) \
+ { \
+ .clk_id = _clk_id, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ }
+
+void tegra_init_from_table(struct tegra_clk_init_table *tbl,
+ struct clk *clks[], int clk_max);
+
+void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
+ struct clk *clks[], int clk_max);
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra20_clock_init(struct device_node *np);
+#else
+static inline void tegra20_clock_init(struct device_node *np) {}
+#endif /* CONFIG_ARCH_TEGRA_2x_SOC */
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra30_clock_init(struct device_node *np);
+#else
+static inline void tegra30_clock_init(struct device_node *np) {}
+#endif /* CONFIG_ARCH_TEGRA_3x_SOC */
+
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+void tegra114_clock_init(struct device_node *np);
+#else
+static inline void tegra114_clock_init(struct device_node *np) {}
+#endif /* CONFIG_ARCH_TEGRA114_SOC */
+
+typedef void (*tegra_clk_apply_init_table_func)(void);
+extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
+
+#endif /* TEGRA_CLK_H */
diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile
new file mode 100644
index 000000000..c6a806ed0
--- /dev/null
+++ b/drivers/clk/ux500/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for ux500 clocks
+#
+
+# Clock types
+obj-y += clk-prcc.o
+obj-y += clk-prcmu.o
+obj-y += clk-sysctrl.o
+
+# Clock definitions
+obj-y += u8500_clk.o
+obj-y += u9540_clk.o
+obj-y += u8540_clk.o
+
+# ABX500 clock driver
+obj-y += abx500-clk.o
diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c
new file mode 100644
index 000000000..a0fca004a
--- /dev/null
+++ b/drivers/clk/ux500/abx500-clk.c
@@ -0,0 +1,138 @@
+/*
+ * abx500 clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include "clk.h"
+
+/* Clock definitions for ab8500 */
+static int ab8500_reg_clks(struct device *dev)
+{
+ int ret;
+ struct clk *clk;
+
+ const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"};
+ u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1};
+ u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK};
+ u8 intclk_reg_bits[] = {
+ 0 ,
+ (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,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, "sysclk", "ab8500-usb.0");
+ clk_register_clkdev(clk, "sysclk", "ab-iddet.0");
+ clk_register_clkdev(clk, "sysclk", "ab85xx-codec.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_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_sysclk4 */
+ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk",
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
+ AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0);
+
+ /* ab_ulpclk */
+ clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL,
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
+ AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
+ 38400000, 9000, CLK_IS_ROOT);
+ clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0");
+
+ /* 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", "ab85xx-codec.0");
+ clk_register_clkdev(clk, NULL, "ab8500-pwm.1");
+
+ /* 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", "ab85xx-codec.0");
+
+ return 0;
+}
+
+/* Clock definitions for ab8540 */
+static int ab8540_reg_clks(struct device *dev)
+{
+ return 0;
+}
+
+/* Clock definitions for ab9540 */
+static int ab9540_reg_clks(struct device *dev)
+{
+ return 0;
+}
+
+static int abx500_clk_probe(struct platform_device *pdev)
+{
+ struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ if (is_ab8500(parent) || is_ab8505(parent)) {
+ ret = ab8500_reg_clks(&pdev->dev);
+ } else if (is_ab8540(parent)) {
+ ret = ab8540_reg_clks(&pdev->dev);
+ } else if (is_ab9540(parent)) {
+ ret = ab9540_reg_clks(&pdev->dev);
+ } else {
+ dev_err(&pdev->dev, "non supported plf id\n");
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
+static struct platform_driver abx500_clk_driver = {
+ .driver = {
+ .name = "abx500-clk",
+ .owner = THIS_MODULE,
+ },
+ .probe = abx500_clk_probe,
+};
+
+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");
+MODULE_DESCRIPTION("ABX500 clk driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c
new file mode 100644
index 000000000..bd4769a84
--- /dev/null
+++ b/drivers/clk/ux500/clk-prcc.c
@@ -0,0 +1,163 @@
+/*
+ * PRCC clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+#define PRCC_PCKEN 0x000
+#define PRCC_PCKDIS 0x004
+#define PRCC_KCKEN 0x008
+#define PRCC_KCKDIS 0x00C
+#define PRCC_PCKSR 0x010
+#define PRCC_KCKSR 0x014
+
+#define to_clk_prcc(_hw) container_of(_hw, struct clk_prcc, hw)
+
+struct clk_prcc {
+ struct clk_hw hw;
+ void __iomem *base;
+ u32 cg_sel;
+ int is_enabled;
+};
+
+/* PRCC clock operations. */
+
+static int clk_prcc_pclk_enable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_PCKEN));
+ while (!(readl(clk->base + PRCC_PCKSR) & clk->cg_sel))
+ cpu_relax();
+
+ clk->is_enabled = 1;
+ return 0;
+}
+
+static void clk_prcc_pclk_disable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_PCKDIS));
+ clk->is_enabled = 0;
+}
+
+static int clk_prcc_kclk_enable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_KCKEN));
+ while (!(readl(clk->base + PRCC_KCKSR) & clk->cg_sel))
+ cpu_relax();
+
+ clk->is_enabled = 1;
+ return 0;
+}
+
+static void clk_prcc_kclk_disable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_KCKDIS));
+ clk->is_enabled = 0;
+}
+
+static int clk_prcc_is_enabled(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+ return clk->is_enabled;
+}
+
+static struct clk_ops clk_prcc_pclk_ops = {
+ .enable = clk_prcc_pclk_enable,
+ .disable = clk_prcc_pclk_disable,
+ .is_enabled = clk_prcc_is_enabled,
+};
+
+static struct clk_ops clk_prcc_kclk_ops = {
+ .enable = clk_prcc_kclk_enable,
+ .disable = clk_prcc_kclk_disable,
+ .is_enabled = clk_prcc_is_enabled,
+};
+
+static struct clk *clk_reg_prcc(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags,
+ struct clk_ops *clk_prcc_ops)
+{
+ struct clk_prcc *clk;
+ struct clk_init_data clk_prcc_init;
+ struct clk *clk_reg;
+
+ if (!name) {
+ pr_err("clk_prcc: %s invalid arguments passed\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk = kzalloc(sizeof(struct clk_prcc), GFP_KERNEL);
+ if (!clk) {
+ pr_err("clk_prcc: %s could not allocate clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ clk->base = ioremap(phy_base, SZ_4K);
+ if (!clk->base)
+ goto free_clk;
+
+ clk->cg_sel = cg_sel;
+ clk->is_enabled = 1;
+
+ clk_prcc_init.name = name;
+ clk_prcc_init.ops = clk_prcc_ops;
+ clk_prcc_init.flags = flags;
+ clk_prcc_init.parent_names = (parent_name ? &parent_name : NULL);
+ clk_prcc_init.num_parents = (parent_name ? 1 : 0);
+ clk->hw.init = &clk_prcc_init;
+
+ clk_reg = clk_register(NULL, &clk->hw);
+ if (IS_ERR_OR_NULL(clk_reg))
+ goto unmap_clk;
+
+ return clk_reg;
+
+unmap_clk:
+ iounmap(clk->base);
+free_clk:
+ kfree(clk);
+ pr_err("clk_prcc: %s failed to register clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+}
+
+struct clk *clk_reg_prcc_pclk(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
+ &clk_prcc_pclk_ops);
+}
+
+struct clk *clk_reg_prcc_kclk(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
+ &clk_prcc_kclk_ops);
+}
diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c
new file mode 100644
index 000000000..293a28854
--- /dev/null
+++ b/drivers/clk/ux500/clk-prcmu.c
@@ -0,0 +1,350 @@
+/*
+ * PRCMU clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
+
+struct clk_prcmu {
+ struct clk_hw hw;
+ u8 cg_sel;
+ int is_prepared;
+ int is_enabled;
+ int opp_requested;
+};
+
+/* PRCMU clock operations. */
+
+static int clk_prcmu_prepare(struct clk_hw *hw)
+{
+ int ret;
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ ret = prcmu_request_clock(clk->cg_sel, true);
+ if (!ret)
+ clk->is_prepared = 1;
+
+ return ret;;
+}
+
+static void clk_prcmu_unprepare(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ if (prcmu_request_clock(clk->cg_sel, false))
+ pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+ __clk_get_name(hw->clk));
+ else
+ clk->is_prepared = 0;
+}
+
+static int clk_prcmu_is_prepared(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return clk->is_prepared;
+}
+
+static int clk_prcmu_enable(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ clk->is_enabled = 1;
+ return 0;
+}
+
+static void clk_prcmu_disable(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ clk->is_enabled = 0;
+}
+
+static int clk_prcmu_is_enabled(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return clk->is_enabled;
+}
+
+static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_clock_rate(clk->cg_sel);
+}
+
+static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_round_clock_rate(clk->cg_sel, rate);
+}
+
+static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_set_clock_rate(clk->cg_sel, rate);
+}
+
+static int clk_prcmu_opp_prepare(struct clk_hw *hw)
+{
+ int err;
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ if (!clk->opp_requested) {
+ err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ (char *)__clk_get_name(hw->clk),
+ 100);
+ if (err) {
+ pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
+ __func__, __clk_get_name(hw->clk));
+ return err;
+ }
+ clk->opp_requested = 1;
+ }
+
+ err = prcmu_request_clock(clk->cg_sel, true);
+ if (err) {
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+ (char *)__clk_get_name(hw->clk));
+ clk->opp_requested = 0;
+ return err;
+ }
+
+ clk->is_prepared = 1;
+ return 0;
+}
+
+static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ if (prcmu_request_clock(clk->cg_sel, false)) {
+ pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+ __clk_get_name(hw->clk));
+ return;
+ }
+
+ if (clk->opp_requested) {
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+ (char *)__clk_get_name(hw->clk));
+ clk->opp_requested = 0;
+ }
+
+ clk->is_prepared = 0;
+}
+
+static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
+{
+ int err;
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ if (!clk->opp_requested) {
+ err = prcmu_request_ape_opp_100_voltage(true);
+ if (err) {
+ pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
+ __func__, __clk_get_name(hw->clk));
+ return err;
+ }
+ clk->opp_requested = 1;
+ }
+
+ err = prcmu_request_clock(clk->cg_sel, true);
+ if (err) {
+ prcmu_request_ape_opp_100_voltage(false);
+ clk->opp_requested = 0;
+ return err;
+ }
+
+ clk->is_prepared = 1;
+ return 0;
+}
+
+static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ if (prcmu_request_clock(clk->cg_sel, false)) {
+ pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+ __clk_get_name(hw->clk));
+ return;
+ }
+
+ if (clk->opp_requested) {
+ prcmu_request_ape_opp_100_voltage(false);
+ clk->opp_requested = 0;
+ }
+
+ clk->is_prepared = 0;
+}
+
+static struct clk_ops clk_prcmu_scalable_ops = {
+ .prepare = clk_prcmu_prepare,
+ .unprepare = clk_prcmu_unprepare,
+ .is_prepared = clk_prcmu_is_prepared,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+ .round_rate = clk_prcmu_round_rate,
+ .set_rate = clk_prcmu_set_rate,
+};
+
+static struct clk_ops clk_prcmu_gate_ops = {
+ .prepare = clk_prcmu_prepare,
+ .unprepare = clk_prcmu_unprepare,
+ .is_prepared = clk_prcmu_is_prepared,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+};
+
+static struct clk_ops clk_prcmu_scalable_rate_ops = {
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+ .round_rate = clk_prcmu_round_rate,
+ .set_rate = clk_prcmu_set_rate,
+};
+
+static struct clk_ops clk_prcmu_rate_ops = {
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+};
+
+static struct clk_ops clk_prcmu_opp_gate_ops = {
+ .prepare = clk_prcmu_opp_prepare,
+ .unprepare = clk_prcmu_opp_unprepare,
+ .is_prepared = clk_prcmu_is_prepared,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+};
+
+static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
+ .prepare = clk_prcmu_opp_volt_prepare,
+ .unprepare = clk_prcmu_opp_volt_unprepare,
+ .is_prepared = clk_prcmu_is_prepared,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+ .round_rate = clk_prcmu_round_rate,
+ .set_rate = clk_prcmu_set_rate,
+};
+
+static struct clk *clk_reg_prcmu(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags,
+ struct clk_ops *clk_prcmu_ops)
+{
+ struct clk_prcmu *clk;
+ struct clk_init_data clk_prcmu_init;
+ struct clk *clk_reg;
+
+ if (!name) {
+ pr_err("clk_prcmu: %s invalid arguments passed\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL);
+ if (!clk) {
+ pr_err("clk_prcmu: %s could not allocate clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ clk->cg_sel = cg_sel;
+ clk->is_prepared = 1;
+ clk->is_enabled = 1;
+ clk->opp_requested = 0;
+ /* "rate" can be used for changing the initial frequency */
+ if (rate)
+ prcmu_set_clock_rate(cg_sel, rate);
+
+ clk_prcmu_init.name = name;
+ clk_prcmu_init.ops = clk_prcmu_ops;
+ clk_prcmu_init.flags = flags;
+ clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
+ clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
+ clk->hw.init = &clk_prcmu_init;
+
+ clk_reg = clk_register(NULL, &clk->hw);
+ if (IS_ERR_OR_NULL(clk_reg))
+ goto free_clk;
+
+ return clk_reg;
+
+free_clk:
+ kfree(clk);
+ pr_err("clk_prcmu: %s failed to register clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+}
+
+struct clk *clk_reg_prcmu_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
+ &clk_prcmu_scalable_ops);
+}
+
+struct clk *clk_reg_prcmu_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
+ &clk_prcmu_gate_ops);
+}
+
+struct clk *clk_reg_prcmu_scalable_rate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
+ &clk_prcmu_scalable_rate_ops);
+}
+
+struct clk *clk_reg_prcmu_rate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
+ &clk_prcmu_rate_ops);
+}
+
+struct clk *clk_reg_prcmu_opp_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
+ &clk_prcmu_opp_gate_ops);
+}
+
+struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
+ &clk_prcmu_opp_volt_scalable_ops);
+}
diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c
new file mode 100644
index 000000000..e364c9d4a
--- /dev/null
+++ b/drivers/clk/ux500/clk-sysctrl.c
@@ -0,0 +1,227 @@
+/*
+ * Sysctrl clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2013 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define SYSCTRL_MAX_NUM_PARENTS 4
+
+#define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw)
+
+struct clk_sysctrl {
+ struct clk_hw hw;
+ struct device *dev;
+ u8 parent_index;
+ u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS];
+ u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS];
+ u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS];
+ unsigned long rate;
+ unsigned long enable_delay_us;
+};
+
+/* Sysctrl clock operations. */
+
+static int clk_sysctrl_prepare(struct clk_hw *hw)
+{
+ int ret;
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+
+ ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0],
+ clk->reg_bits[0]);
+
+ if (!ret && clk->enable_delay_us)
+ usleep_range(clk->enable_delay_us, clk->enable_delay_us);
+
+ return ret;
+}
+
+static void clk_sysctrl_unprepare(struct clk_hw *hw)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0]))
+ dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n",
+ __func__, __clk_get_name(hw->clk));
+}
+
+static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ return clk->rate;
+}
+
+static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ u8 old_index = clk->parent_index;
+ int ret = 0;
+
+ if (clk->reg_sel[old_index]) {
+ ret = ab8500_sysctrl_clear(clk->reg_sel[old_index],
+ clk->reg_mask[old_index]);
+ if (ret)
+ return ret;
+ }
+
+ if (clk->reg_sel[index]) {
+ ret = ab8500_sysctrl_write(clk->reg_sel[index],
+ clk->reg_mask[index],
+ clk->reg_bits[index]);
+ if (ret) {
+ if (clk->reg_sel[old_index])
+ ab8500_sysctrl_write(clk->reg_sel[old_index],
+ clk->reg_mask[old_index],
+ clk->reg_bits[old_index]);
+ return ret;
+ }
+ }
+ clk->parent_index = index;
+
+ return ret;
+}
+
+static u8 clk_sysctrl_get_parent(struct clk_hw *hw)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ return clk->parent_index;
+}
+
+static struct clk_ops clk_sysctrl_gate_ops = {
+ .prepare = clk_sysctrl_prepare,
+ .unprepare = clk_sysctrl_unprepare,
+};
+
+static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = {
+ .prepare = clk_sysctrl_prepare,
+ .unprepare = clk_sysctrl_unprepare,
+ .recalc_rate = clk_sysctrl_recalc_rate,
+};
+
+static struct clk_ops clk_sysctrl_set_parent_ops = {
+ .set_parent = clk_sysctrl_set_parent,
+ .get_parent = clk_sysctrl_get_parent,
+};
+
+static struct clk *clk_reg_sysctrl(struct device *dev,
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ u16 *reg_sel,
+ u8 *reg_mask,
+ u8 *reg_bits,
+ unsigned long rate,
+ unsigned long enable_delay_us,
+ unsigned long flags,
+ struct clk_ops *clk_sysctrl_ops)
+{
+ struct clk_sysctrl *clk;
+ struct clk_init_data clk_sysctrl_init;
+ struct clk *clk_reg;
+ int i;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) {
+ dev_err(dev, "clk_sysctrl: invalid arguments passed\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL);
+ if (!clk) {
+ dev_err(dev, "clk_sysctrl: could not allocate clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* set main clock registers */
+ clk->reg_sel[0] = reg_sel[0];
+ clk->reg_bits[0] = reg_bits[0];
+ clk->reg_mask[0] = reg_mask[0];
+
+ /* handle clocks with more than one parent */
+ for (i = 1; i < num_parents; i++) {
+ clk->reg_sel[i] = reg_sel[i];
+ clk->reg_bits[i] = reg_bits[i];
+ clk->reg_mask[i] = reg_mask[i];
+ }
+
+ clk->parent_index = 0;
+ clk->rate = rate;
+ clk->enable_delay_us = enable_delay_us;
+ clk->dev = dev;
+
+ clk_sysctrl_init.name = name;
+ clk_sysctrl_init.ops = clk_sysctrl_ops;
+ clk_sysctrl_init.flags = flags;
+ clk_sysctrl_init.parent_names = parent_names;
+ clk_sysctrl_init.num_parents = num_parents;
+ clk->hw.init = &clk_sysctrl_init;
+
+ clk_reg = devm_clk_register(clk->dev, &clk->hw);
+ if (IS_ERR(clk_reg))
+ dev_err(dev, "clk_sysctrl: clk_register failed\n");
+
+ return clk_reg;
+}
+
+struct clk *clk_reg_sysctrl_gate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long enable_delay_us,
+ unsigned long flags)
+{
+ const char **parent_names = (parent_name ? &parent_name : NULL);
+ u8 num_parents = (parent_name ? 1 : 0);
+
+ return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+ &reg_sel, &reg_mask, &reg_bits, 0, enable_delay_us,
+ flags, &clk_sysctrl_gate_ops);
+}
+
+struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long rate,
+ unsigned long enable_delay_us,
+ unsigned long flags)
+{
+ const char **parent_names = (parent_name ? &parent_name : NULL);
+ u8 num_parents = (parent_name ? 1 : 0);
+
+ return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+ &reg_sel, &reg_mask, &reg_bits,
+ rate, enable_delay_us, flags,
+ &clk_sysctrl_gate_fixed_rate_ops);
+}
+
+struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ u16 *reg_sel,
+ u8 *reg_mask,
+ u8 *reg_bits,
+ unsigned long flags)
+{
+ return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+ reg_sel, reg_mask, reg_bits, 0, 0, flags,
+ &clk_sysctrl_set_parent_ops);
+}
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h
new file mode 100644
index 000000000..a2bb92d85
--- /dev/null
+++ b/drivers/clk/ux500/clk.h
@@ -0,0 +1,90 @@
+/*
+ * Clocks for ux500 platforms
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __UX500_CLK_H
+#define __UX500_CLK_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+struct clk *clk_reg_prcc_pclk(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcc_kclk(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_scalable_rate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_rate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_opp_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags);
+
+struct clk *clk_reg_sysctrl_gate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long enable_delay_us,
+ unsigned long flags);
+
+struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long rate,
+ unsigned long enable_delay_us,
+ unsigned long flags);
+
+struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ u16 *reg_sel,
+ u8 *reg_mask,
+ u8 *reg_bits,
+ unsigned long flags);
+
+#endif /* __UX500_CLK_H */
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
new file mode 100644
index 000000000..80069c370
--- /dev/null
+++ b/drivers/clk/ux500/u8500_clk.c
@@ -0,0 +1,525 @@
+/*
+ * Clock definitions for u8500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/platform_data/clk-ux500.h>
+#include "clk.h"
+
+void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base,
+ u32 clkrst5_base, u32 clkrst6_base)
+{
+ struct prcmu_fw_version *fw_version;
+ const char *sgaclk_parent = NULL;
+ struct clk *clk;
+
+ /* Clock sources */
+ clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0,
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "soc0_pll", NULL);
+
+ clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1,
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "soc1_pll", NULL);
+
+ clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR,
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "ddr_pll", NULL);
+
+ /* FIXME: Add sys, ulp and int clocks here. */
+
+ clk = clk_register_fixed_rate(NULL, "rtc32k", "NULL",
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED,
+ 32768);
+ clk_register_clkdev(clk, "clk32k", NULL);
+ clk_register_clkdev(clk, "apb_pclk", "rtc-pl031");
+
+ /* PRCMU clocks */
+ fw_version = prcmu_get_fw_version();
+ if (fw_version != NULL) {
+ switch (fw_version->project) {
+ case PRCMU_FW_PROJECT_U8500_C2:
+ case PRCMU_FW_PROJECT_U8520:
+ case PRCMU_FW_PROJECT_U8420:
+ sgaclk_parent = "soc0_pll";
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (sgaclk_parent)
+ clk = clk_reg_prcmu_gate("sgclk", sgaclk_parent,
+ PRCMU_SGACLK, 0);
+ else
+ clk = clk_reg_prcmu_gate("sgclk", NULL,
+ PRCMU_SGACLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "mali");
+
+ clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "UART");
+
+ clk = clk_reg_prcmu_gate("msp02clk", NULL, PRCMU_MSP02CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "MSP02");
+
+ clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "MSP1");
+
+ clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "I2C");
+
+ clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "slim");
+
+ clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH1");
+
+ clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH2");
+
+ clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH3");
+
+ clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH5");
+
+ clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH6");
+
+ clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH7");
+
+ clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "lcd");
+ clk_register_clkdev(clk, "lcd", "mcde");
+
+ clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "bml");
+
+ clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "hdmi");
+ clk_register_clkdev(clk, "hdmi", "mcde");
+
+ clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "apeat");
+
+ clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "apetrace");
+
+ clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "mcde");
+ clk_register_clkdev(clk, "mcde", "mcde");
+ clk_register_clkdev(clk, "dsisys", "dsilink.0");
+ clk_register_clkdev(clk, "dsisys", "dsilink.1");
+ clk_register_clkdev(clk, "dsisys", "dsilink.2");
+
+ clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "ipi2");
+
+ clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "dsialt");
+
+ clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "dma40.0");
+
+ clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "b2r2");
+ clk_register_clkdev(clk, NULL, "b2r2_core");
+ clk_register_clkdev(clk, NULL, "U8500-B2R2.0");
+
+ clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "tv");
+ clk_register_clkdev(clk, "tv", "mcde");
+
+ clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "SSP");
+
+ clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "rngclk");
+
+ clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "uicc");
+
+ clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "mtu0");
+ clk_register_clkdev(clk, NULL, "mtu1");
+
+ clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, PRCMU_SDMMCCLK,
+ 100000000,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdmmc");
+
+ clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk",
+ PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsihs2", "mcde");
+ clk_register_clkdev(clk, "dsihs2", "dsilink.2");
+
+
+ clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll",
+ PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsihs0", "mcde");
+ clk_register_clkdev(clk, "dsihs0", "dsilink.0");
+
+ clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll",
+ PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsihs1", "mcde");
+ clk_register_clkdev(clk, "dsihs1", "dsilink.1");
+
+ clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk",
+ PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsilp0", "dsilink.0");
+ clk_register_clkdev(clk, "dsilp0", "mcde");
+
+ clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk",
+ PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsilp1", "dsilink.1");
+ clk_register_clkdev(clk, "dsilp1", "mcde");
+
+ clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk",
+ PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsilp2", "dsilink.2");
+ clk_register_clkdev(clk, "dsilp2", "mcde");
+
+ clk = clk_reg_prcmu_scalable_rate("armss", NULL,
+ PRCMU_ARMSS, 0, CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "armss", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "smp_twd", "armss",
+ CLK_IGNORE_UNUSED, 1, 2);
+ clk_register_clkdev(clk, NULL, "smp_twd");
+
+ /*
+ * FIXME: Add special handled PRCMU clocks here:
+ * 1. clkout0yuv, use PRCMU as parent + need regulator + pinctrl.
+ * 2. ab9540_clkout1yuv, see clkout0yuv
+ */
+
+ /* PRCC P-clocks */
+ clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "apb_pclk", "uart0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base,
+ BIT(1), 0);
+ clk_register_clkdev(clk, "apb_pclk", "uart1");
+
+ clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base,
+ BIT(2), 0);
+ clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.1");
+
+ clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base,
+ BIT(3), 0);
+ clk_register_clkdev(clk, "apb_pclk", "msp0");
+ clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base,
+ BIT(4), 0);
+ clk_register_clkdev(clk, "apb_pclk", "msp1");
+ clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.1");
+
+ clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base,
+ BIT(5), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base,
+ BIT(6), 0);
+ clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2");
+
+ clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base,
+ BIT(7), 0);
+ clk_register_clkdev(clk, NULL, "spi3");
+
+ clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base,
+ BIT(8), 0);
+ clk_register_clkdev(clk, "apb_pclk", "slimbus0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base,
+ BIT(9), 0);
+ clk_register_clkdev(clk, NULL, "gpio.0");
+ clk_register_clkdev(clk, NULL, "gpio.1");
+ clk_register_clkdev(clk, NULL, "gpioblock0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base,
+ BIT(10), 0);
+ clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.4");
+
+ clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base,
+ BIT(11), 0);
+ clk_register_clkdev(clk, "apb_pclk", "msp3");
+ clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.3");
+
+ clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.3");
+
+ clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base,
+ BIT(1), 0);
+ clk_register_clkdev(clk, NULL, "spi2");
+
+ clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base,
+ BIT(2), 0);
+ clk_register_clkdev(clk, NULL, "spi1");
+
+ clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base,
+ BIT(3), 0);
+ clk_register_clkdev(clk, NULL, "pwl");
+
+ clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base,
+ BIT(4), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi4");
+
+ clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base,
+ BIT(5), 0);
+ clk_register_clkdev(clk, "apb_pclk", "msp2");
+ clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.2");
+
+ clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base,
+ BIT(6), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi1");
+
+ clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base,
+ BIT(7), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi3");
+
+ clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base,
+ BIT(8), 0);
+ clk_register_clkdev(clk, NULL, "spi0");
+
+ clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base,
+ BIT(9), 0);
+ clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0");
+
+ clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base,
+ BIT(10), 0);
+ clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0");
+
+ clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base,
+ BIT(11), 0);
+ clk_register_clkdev(clk, NULL, "gpio.6");
+ clk_register_clkdev(clk, NULL, "gpio.7");
+ clk_register_clkdev(clk, NULL, "gpioblock1");
+
+ clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base,
+ BIT(12), 0);
+
+ clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "fsmc", NULL);
+ clk_register_clkdev(clk, NULL, "smsc911x.0");
+
+ clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base,
+ BIT(1), 0);
+ clk_register_clkdev(clk, "apb_pclk", "ssp0");
+
+ clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base,
+ BIT(2), 0);
+ clk_register_clkdev(clk, "apb_pclk", "ssp1");
+
+ clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base,
+ BIT(3), 0);
+ clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.0");
+
+ clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base,
+ BIT(4), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi2");
+
+ clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base,
+ BIT(5), 0);
+ clk_register_clkdev(clk, "apb_pclk", "ske");
+ clk_register_clkdev(clk, "apb_pclk", "nmk-ske-keypad");
+
+ clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base,
+ BIT(6), 0);
+ clk_register_clkdev(clk, "apb_pclk", "uart2");
+
+ clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base,
+ BIT(7), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi5");
+
+ clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base,
+ BIT(8), 0);
+ clk_register_clkdev(clk, NULL, "gpio.2");
+ clk_register_clkdev(clk, NULL, "gpio.3");
+ clk_register_clkdev(clk, NULL, "gpio.4");
+ clk_register_clkdev(clk, NULL, "gpio.5");
+ clk_register_clkdev(clk, NULL, "gpioblock2");
+
+ clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "usb", "musb-ux500.0");
+
+ clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base,
+ BIT(1), 0);
+ clk_register_clkdev(clk, NULL, "gpio.8");
+ clk_register_clkdev(clk, NULL, "gpioblock3");
+
+ clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "apb_pclk", "rng");
+
+ clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base,
+ BIT(1), 0);
+ clk_register_clkdev(clk, NULL, "cryp0");
+ clk_register_clkdev(clk, NULL, "cryp1");
+
+ clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base,
+ BIT(2), 0);
+ clk_register_clkdev(clk, NULL, "hash0");
+
+ clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base,
+ BIT(3), 0);
+ clk_register_clkdev(clk, NULL, "pka");
+
+ clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base,
+ BIT(4), 0);
+ clk_register_clkdev(clk, NULL, "hash1");
+
+ clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base,
+ BIT(5), 0);
+ clk_register_clkdev(clk, NULL, "cfgreg");
+
+ clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base,
+ BIT(6), 0);
+ clk_register_clkdev(clk, "apb_pclk", "mtu0");
+
+ clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base,
+ BIT(7), 0);
+ clk_register_clkdev(clk, "apb_pclk", "mtu1");
+
+ /* PRCC K-clocks
+ *
+ * FIXME: Some drivers requires PERPIH[n| to be automatically enabled
+ * by enabling just the K-clock, even if it is not a valid parent to
+ * the K-clock. Until drivers get fixed we might need some kind of
+ * "parent muxed join".
+ */
+
+ /* Periph1 */
+ clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk",
+ clkrst1_base, BIT(0), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "uart0");
+
+ clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk",
+ clkrst1_base, BIT(1), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "uart1");
+
+ clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk",
+ clkrst1_base, BIT(2), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "nmk-i2c.1");
+
+ clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk",
+ clkrst1_base, BIT(3), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "msp0");
+ clk_register_clkdev(clk, NULL, "ux500-msp-i2s.0");
+
+ clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk",
+ clkrst1_base, BIT(4), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "msp1");
+ clk_register_clkdev(clk, NULL, "ux500-msp-i2s.1");
+
+ clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk",
+ clkrst1_base, BIT(5), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi0");
+
+ clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk",
+ clkrst1_base, BIT(6), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "nmk-i2c.2");
+
+ clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk",
+ clkrst1_base, BIT(8), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "slimbus0");
+
+ clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk",
+ clkrst1_base, BIT(9), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "nmk-i2c.4");
+
+ clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk",
+ clkrst1_base, BIT(10), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "msp3");
+ clk_register_clkdev(clk, NULL, "ux500-msp-i2s.3");
+
+ /* Periph2 */
+ clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk",
+ clkrst2_base, BIT(0), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "nmk-i2c.3");
+
+ clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk",
+ clkrst2_base, BIT(2), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi4");
+
+ clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk",
+ clkrst2_base, BIT(3), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "msp2");
+ clk_register_clkdev(clk, NULL, "ux500-msp-i2s.2");
+
+ clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk",
+ clkrst2_base, BIT(4), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi1");
+
+ clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk",
+ clkrst2_base, BIT(5), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi3");
+
+ /* Note that rate is received from parent. */
+ clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk",
+ clkrst2_base, BIT(6),
+ CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
+ clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk",
+ clkrst2_base, BIT(7),
+ CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
+
+ /* Periph3 */
+ clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk",
+ clkrst3_base, BIT(1), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "ssp0");
+
+ clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk",
+ clkrst3_base, BIT(2), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "ssp1");
+
+ clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk",
+ clkrst3_base, BIT(3), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "nmk-i2c.0");
+
+ clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk",
+ clkrst3_base, BIT(4), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi2");
+
+ clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k",
+ clkrst3_base, BIT(5), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "ske");
+ clk_register_clkdev(clk, NULL, "nmk-ske-keypad");
+
+ clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk",
+ clkrst3_base, BIT(6), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "uart2");
+
+ clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk",
+ clkrst3_base, BIT(7), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi5");
+
+ /* Periph6 */
+ clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk",
+ clkrst6_base, BIT(0), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "rng");
+}
diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c
new file mode 100644
index 000000000..10adfd2ea
--- /dev/null
+++ b/drivers/clk/ux500/u8540_clk.c
@@ -0,0 +1,21 @@
+/*
+ * Clock definitions for u8540 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/platform_data/clk-ux500.h>
+
+#include "clk.h"
+
+void u8540_clk_init(void)
+{
+ /* register clocks here */
+}
diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c
new file mode 100644
index 000000000..dbc0191e1
--- /dev/null
+++ b/drivers/clk/ux500/u9540_clk.c
@@ -0,0 +1,21 @@
+/*
+ * Clock definitions for u9540 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/platform_data/clk-ux500.h>
+
+#include "clk.h"
+
+void u9540_clk_init(void)
+{
+ /* register clocks here */
+}
diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile
new file mode 100644
index 000000000..c16ca7871
--- /dev/null
+++ b/drivers/clk/versatile/Makefile
@@ -0,0 +1,7 @@
+# Makefile for Versatile-specific clocks
+obj-$(CONFIG_ICST) += clk-icst.o
+obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
+obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
+obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
+obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o
+obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
new file mode 100644
index 000000000..f5e4c21b3
--- /dev/null
+++ b/drivers/clk/versatile/clk-icst.c
@@ -0,0 +1,148 @@
+/*
+ * Driver for the ICST307 VCO clock found in the ARM Reference designs.
+ * We wrap the custom interface from <asm/hardware/icst.h> into the generic
+ * clock framework.
+ *
+ * Copyright (C) 2012 Linus Walleij
+ *
+ * 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.
+ *
+ * TODO: when all ARM reference designs are migrated to generic clocks, the
+ * ICST clock code from the ARM tree should probably be merged into this
+ * file.
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+
+#include "clk-icst.h"
+
+/**
+ * struct clk_icst - ICST VCO clock wrapper
+ * @hw: corresponding clock hardware entry
+ * @vcoreg: VCO register address
+ * @lockreg: VCO lock register address
+ * @params: parameters for this ICST instance
+ * @rate: current rate
+ */
+struct clk_icst {
+ struct clk_hw hw;
+ void __iomem *vcoreg;
+ void __iomem *lockreg;
+ const struct icst_params *params;
+ unsigned long rate;
+};
+
+#define to_icst(_hw) container_of(_hw, struct clk_icst, hw)
+
+/**
+ * vco_get() - get ICST VCO settings from a certain register
+ * @vcoreg: register containing the VCO settings
+ */
+static struct icst_vco vco_get(void __iomem *vcoreg)
+{
+ u32 val;
+ struct icst_vco vco;
+
+ val = readl(vcoreg);
+ vco.v = val & 0x1ff;
+ vco.r = (val >> 9) & 0x7f;
+ vco.s = (val >> 16) & 03;
+ return vco;
+}
+
+/**
+ * vco_set() - commit changes to an ICST VCO
+ * @locreg: register to poke to unlock the VCO for writing
+ * @vcoreg: register containing the VCO settings
+ * @vco: ICST VCO parameters to commit
+ */
+static void vco_set(void __iomem *lockreg,
+ void __iomem *vcoreg,
+ struct icst_vco vco)
+{
+ u32 val;
+
+ val = readl(vcoreg) & ~0x7ffff;
+ val |= vco.v | (vco.r << 9) | (vco.s << 16);
+
+ /* This magic unlocks the VCO so it can be controlled */
+ writel(0xa05f, lockreg);
+ writel(val, vcoreg);
+ /* This locks the VCO again */
+ writel(0, lockreg);
+}
+
+
+static unsigned long icst_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_icst *icst = to_icst(hw);
+ struct icst_vco vco;
+
+ vco = vco_get(icst->vcoreg);
+ icst->rate = icst_hz(icst->params, vco);
+ return icst->rate;
+}
+
+static long icst_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_icst *icst = to_icst(hw);
+ struct icst_vco vco;
+
+ vco = icst_hz_to_vco(icst->params, rate);
+ return icst_hz(icst->params, vco);
+}
+
+static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_icst *icst = to_icst(hw);
+ struct icst_vco vco;
+
+ vco = icst_hz_to_vco(icst->params, rate);
+ icst->rate = icst_hz(icst->params, vco);
+ vco_set(icst->lockreg, icst->vcoreg, vco);
+ return 0;
+}
+
+static const struct clk_ops icst_ops = {
+ .recalc_rate = icst_recalc_rate,
+ .round_rate = icst_round_rate,
+ .set_rate = icst_set_rate,
+};
+
+struct clk *icst_clk_register(struct device *dev,
+ const struct clk_icst_desc *desc,
+ void __iomem *base)
+{
+ struct clk *clk;
+ struct clk_icst *icst;
+ struct clk_init_data init;
+
+ icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL);
+ if (!icst) {
+ pr_err("could not allocate ICST clock!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ init.name = "icst";
+ init.ops = &icst_ops;
+ init.flags = CLK_IS_ROOT;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ icst->hw.init = &init;
+ icst->params = desc->params;
+ icst->vcoreg = base + desc->vco_offset;
+ icst->lockreg = base + desc->lock_offset;
+
+ clk = clk_register(dev, &icst->hw);
+ if (IS_ERR(clk))
+ kfree(icst);
+
+ return clk;
+}
diff --git a/drivers/clk/versatile/clk-icst.h b/drivers/clk/versatile/clk-icst.h
new file mode 100644
index 000000000..dad51b6ff
--- /dev/null
+++ b/drivers/clk/versatile/clk-icst.h
@@ -0,0 +1,18 @@
+#include <asm/hardware/icst.h>
+
+/**
+ * struct clk_icst_desc - descriptor for the ICST VCO
+ * @params: ICST parameters
+ * @vco_offset: offset to the ICST VCO from the provided memory base
+ * @lock_offset: offset to the ICST VCO locking register from the provided
+ * memory base
+ */
+struct clk_icst_desc {
+ const struct icst_params *params;
+ u32 vco_offset;
+ u32 lock_offset;
+};
+
+struct clk *icst_clk_register(struct device *dev,
+ const struct clk_icst_desc *desc,
+ void __iomem *base);
diff --git a/drivers/clk/versatile/clk-impd1.c b/drivers/clk/versatile/clk-impd1.c
new file mode 100644
index 000000000..369139af2
--- /dev/null
+++ b/drivers/clk/versatile/clk-impd1.c
@@ -0,0 +1,97 @@
+/*
+ * Clock driver for the ARM Integrator/IM-PD1 board
+ * Copyright (C) 2012 Linus Walleij
+ *
+ * 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.
+ */
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_data/clk-integrator.h>
+
+#include <mach/impd1.h>
+
+#include "clk-icst.h"
+
+struct impd1_clk {
+ struct clk *vcoclk;
+ struct clk *uartclk;
+ struct clk_lookup *clks[3];
+};
+
+static struct impd1_clk impd1_clks[4];
+
+/*
+ * There are two VCO's on the IM-PD1 but only one is used by the
+ * kernel, that is why we are only implementing the control of
+ * IMPD1_OSC1 here.
+ */
+
+static const struct icst_params impd1_vco_params = {
+ .ref = 24000000, /* 24 MHz */
+ .vco_max = ICST525_VCO_MAX_3V,
+ .vco_min = ICST525_VCO_MIN,
+ .vd_min = 12,
+ .vd_max = 519,
+ .rd_min = 3,
+ .rd_max = 120,
+ .s2div = icst525_s2div,
+ .idx2s = icst525_idx2s,
+};
+
+static const struct clk_icst_desc impd1_icst1_desc = {
+ .params = &impd1_vco_params,
+ .vco_offset = IMPD1_OSC1,
+ .lock_offset = IMPD1_LOCK,
+};
+
+/**
+ * integrator_impd1_clk_init() - set up the integrator clock tree
+ * @base: base address of the logic module (LM)
+ * @id: the ID of this LM
+ */
+void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
+{
+ struct impd1_clk *imc;
+ struct clk *clk;
+ int i;
+
+ if (id > 3) {
+ pr_crit("no more than 4 LMs can be attached\n");
+ return;
+ }
+ imc = &impd1_clks[id];
+
+ clk = icst_clk_register(NULL, &impd1_icst1_desc, base);
+ imc->vcoclk = clk;
+ imc->clks[0] = clkdev_alloc(clk, NULL, "lm%x:01000", id);
+
+ /* UART reference clock */
+ clk = clk_register_fixed_rate(NULL, "uartclk", NULL, CLK_IS_ROOT,
+ 14745600);
+ imc->uartclk = clk;
+ imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:00100", id);
+ imc->clks[2] = clkdev_alloc(clk, NULL, "lm%x:00200", id);
+
+ for (i = 0; i < ARRAY_SIZE(imc->clks); i++)
+ clkdev_add(imc->clks[i]);
+}
+
+void integrator_impd1_clk_exit(unsigned int id)
+{
+ int i;
+ struct impd1_clk *imc;
+
+ if (id > 3)
+ return;
+ imc = &impd1_clks[id];
+
+ for (i = 0; i < ARRAY_SIZE(imc->clks); i++)
+ clkdev_drop(imc->clks[i]);
+ clk_unregister(imc->uartclk);
+ clk_unregister(imc->vcoclk);
+}
diff --git a/drivers/clk/versatile/clk-integrator.c b/drivers/clk/versatile/clk-integrator.c
new file mode 100644
index 000000000..08593b4ee
--- /dev/null
+++ b/drivers/clk/versatile/clk-integrator.c
@@ -0,0 +1,84 @@
+/*
+ * Clock driver for the ARM Integrator/AP and Integrator/CP boards
+ * Copyright (C) 2012 Linus Walleij
+ *
+ * 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.
+ */
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/platform_data/clk-integrator.h>
+
+#include <mach/hardware.h>
+#include <mach/platform.h>
+
+#include "clk-icst.h"
+
+/*
+ * Implementation of the ARM Integrator/AP and Integrator/CP clock tree.
+ * Inspired by portions of:
+ * plat-versatile/clock.c and plat-versatile/include/plat/clock.h
+ */
+
+static const struct icst_params cp_auxvco_params = {
+ .ref = 24000000,
+ .vco_max = ICST525_VCO_MAX_5V,
+ .vco_min = ICST525_VCO_MIN,
+ .vd_min = 8,
+ .vd_max = 263,
+ .rd_min = 3,
+ .rd_max = 65,
+ .s2div = icst525_s2div,
+ .idx2s = icst525_idx2s,
+};
+
+static const struct clk_icst_desc __initdata cp_icst_desc = {
+ .params = &cp_auxvco_params,
+ .vco_offset = 0x1c,
+ .lock_offset = INTEGRATOR_HDR_LOCK_OFFSET,
+};
+
+/*
+ * integrator_clk_init() - set up the integrator clock tree
+ * @is_cp: pass true if it's the Integrator/CP else AP is assumed
+ */
+void __init integrator_clk_init(bool is_cp)
+{
+ struct clk *clk;
+
+ /* APB clock dummy */
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
+ clk_register_clkdev(clk, "apb_pclk", NULL);
+
+ /* UART reference clock */
+ clk = clk_register_fixed_rate(NULL, "uartclk", NULL, CLK_IS_ROOT,
+ 14745600);
+ clk_register_clkdev(clk, NULL, "uart0");
+ clk_register_clkdev(clk, NULL, "uart1");
+ if (is_cp)
+ clk_register_clkdev(clk, NULL, "mmci");
+
+ /* 24 MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT,
+ 24000000);
+ clk_register_clkdev(clk, NULL, "kmi0");
+ clk_register_clkdev(clk, NULL, "kmi1");
+ if (!is_cp)
+ clk_register_clkdev(clk, NULL, "ap_timer");
+
+ if (!is_cp)
+ return;
+
+ /* 1 MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT,
+ 1000000);
+ clk_register_clkdev(clk, NULL, "sp804");
+
+ /* ICST VCO clock used on the Integrator/CP CLCD */
+ clk = icst_clk_register(NULL, &cp_icst_desc,
+ __io_address(INTEGRATOR_HDR_BASE));
+ clk_register_clkdev(clk, NULL, "clcd");
+}
diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c
new file mode 100644
index 000000000..cda07e70a
--- /dev/null
+++ b/drivers/clk/versatile/clk-realview.c
@@ -0,0 +1,93 @@
+/*
+ * Clock driver for the ARM RealView boards
+ * Copyright (C) 2012 Linus Walleij
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+
+#include <mach/hardware.h>
+#include <mach/platform.h>
+
+#include "clk-icst.h"
+
+/*
+ * Implementation of the ARM RealView clock trees.
+ */
+
+static const struct icst_params realview_oscvco_params = {
+ .ref = 24000000,
+ .vco_max = ICST307_VCO_MAX,
+ .vco_min = ICST307_VCO_MIN,
+ .vd_min = 4 + 8,
+ .vd_max = 511 + 8,
+ .rd_min = 1 + 2,
+ .rd_max = 127 + 2,
+ .s2div = icst307_s2div,
+ .idx2s = icst307_idx2s,
+};
+
+static const struct clk_icst_desc __initdata realview_osc0_desc = {
+ .params = &realview_oscvco_params,
+ .vco_offset = REALVIEW_SYS_OSC0_OFFSET,
+ .lock_offset = REALVIEW_SYS_LOCK_OFFSET,
+};
+
+static const struct clk_icst_desc __initdata realview_osc4_desc = {
+ .params = &realview_oscvco_params,
+ .vco_offset = REALVIEW_SYS_OSC4_OFFSET,
+ .lock_offset = REALVIEW_SYS_LOCK_OFFSET,
+};
+
+/*
+ * realview_clk_init() - set up the RealView clock tree
+ */
+void __init realview_clk_init(void __iomem *sysbase, bool is_pb1176)
+{
+ struct clk *clk;
+
+ /* APB clock dummy */
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
+ clk_register_clkdev(clk, "apb_pclk", NULL);
+
+ /* 24 MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT,
+ 24000000);
+ clk_register_clkdev(clk, NULL, "dev:uart0");
+ clk_register_clkdev(clk, NULL, "dev:uart1");
+ clk_register_clkdev(clk, NULL, "dev:uart2");
+ clk_register_clkdev(clk, NULL, "fpga:kmi0");
+ clk_register_clkdev(clk, NULL, "fpga:kmi1");
+ clk_register_clkdev(clk, NULL, "fpga:mmc0");
+ clk_register_clkdev(clk, NULL, "dev:ssp0");
+ if (is_pb1176) {
+ /*
+ * UART3 is on the dev chip in PB1176
+ * UART4 only exists in PB1176
+ */
+ clk_register_clkdev(clk, NULL, "dev:uart3");
+ clk_register_clkdev(clk, NULL, "dev:uart4");
+ } else
+ clk_register_clkdev(clk, NULL, "fpga:uart3");
+
+
+ /* 1 MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT,
+ 1000000);
+ clk_register_clkdev(clk, NULL, "sp804");
+
+ /* ICST VCO clock */
+ if (is_pb1176)
+ clk = icst_clk_register(NULL, &realview_osc0_desc, sysbase);
+ else
+ clk = icst_clk_register(NULL, &realview_osc4_desc, sysbase);
+
+ clk_register_clkdev(clk, NULL, "dev:clcd");
+ clk_register_clkdev(clk, NULL, "issp:clcd");
+}
diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c
new file mode 100644
index 000000000..bf9b15a58
--- /dev/null
+++ b/drivers/clk/versatile/clk-sp810.c
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#include <linux/amba/sp810.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define to_clk_sp810_timerclken(_hw) \
+ container_of(_hw, struct clk_sp810_timerclken, hw)
+
+struct clk_sp810;
+
+struct clk_sp810_timerclken {
+ struct clk_hw hw;
+ struct clk *clk;
+ struct clk_sp810 *sp810;
+ int channel;
+};
+
+struct clk_sp810 {
+ struct device_node *node;
+ int refclk_index, timclk_index;
+ void __iomem *base;
+ spinlock_t lock;
+ struct clk_sp810_timerclken timerclken[4];
+ struct clk *refclk;
+ struct clk *timclk;
+};
+
+static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ u32 val = readl(timerclken->sp810->base + SCCTRL);
+
+ return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel)));
+}
+
+static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ struct clk_sp810 *sp810 = timerclken->sp810;
+ u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel);
+ unsigned long flags = 0;
+
+ if (WARN_ON(index > 1))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sp810->lock, flags);
+
+ val = readl(sp810->base + SCCTRL);
+ val &= ~(1 << shift);
+ val |= index << shift;
+ writel(val, sp810->base + SCCTRL);
+
+ spin_unlock_irqrestore(&sp810->lock, flags);
+
+ return 0;
+}
+
+/*
+ * FIXME - setting the parent every time .prepare is invoked is inefficient.
+ * This is better handled by a dedicated clock tree configuration mechanism at
+ * init-time. Revisit this later when such a mechanism exists
+ */
+static int clk_sp810_timerclken_prepare(struct clk_hw *hw)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ struct clk_sp810 *sp810 = timerclken->sp810;
+ struct clk *old_parent = __clk_get_parent(hw->clk);
+ struct clk *new_parent;
+
+ if (!sp810->refclk)
+ sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index);
+
+ if (!sp810->timclk)
+ sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index);
+
+ if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk)))
+ return -ENOENT;
+
+ /* Select fastest parent */
+ if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk))
+ new_parent = sp810->refclk;
+ else
+ new_parent = sp810->timclk;
+
+ /* Switch the parent if necessary */
+ if (old_parent != new_parent) {
+ clk_prepare(new_parent);
+ clk_set_parent(hw->clk, new_parent);
+ clk_unprepare(old_parent);
+ }
+
+ return 0;
+}
+
+static void clk_sp810_timerclken_unprepare(struct clk_hw *hw)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ struct clk_sp810 *sp810 = timerclken->sp810;
+
+ clk_put(sp810->timclk);
+ clk_put(sp810->refclk);
+}
+
+static const struct clk_ops clk_sp810_timerclken_ops = {
+ .prepare = clk_sp810_timerclken_prepare,
+ .unprepare = clk_sp810_timerclken_unprepare,
+ .get_parent = clk_sp810_timerclken_get_parent,
+ .set_parent = clk_sp810_timerclken_set_parent,
+};
+
+struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct clk_sp810 *sp810 = data;
+
+ if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
+ ARRAY_SIZE(sp810->timerclken)))
+ return NULL;
+
+ return sp810->timerclken[clkspec->args[0]].clk;
+}
+
+void __init clk_sp810_of_setup(struct device_node *node)
+{
+ struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL);
+ const char *parent_names[2];
+ char name[12];
+ struct clk_init_data init;
+ int i;
+
+ if (!sp810) {
+ pr_err("Failed to allocate memory for SP810!\n");
+ return;
+ }
+
+ sp810->refclk_index = of_property_match_string(node, "clock-names",
+ "refclk");
+ parent_names[0] = of_clk_get_parent_name(node, sp810->refclk_index);
+
+ sp810->timclk_index = of_property_match_string(node, "clock-names",
+ "timclk");
+ parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index);
+
+ if (parent_names[0] <= 0 || parent_names[1] <= 0) {
+ pr_warn("Failed to obtain parent clocks for SP810!\n");
+ return;
+ }
+
+ sp810->node = node;
+ sp810->base = of_iomap(node, 0);
+ spin_lock_init(&sp810->lock);
+
+ init.name = name;
+ init.ops = &clk_sp810_timerclken_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = ARRAY_SIZE(parent_names);
+
+ for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
+ snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+
+ sp810->timerclken[i].sp810 = sp810;
+ sp810->timerclken[i].channel = i;
+ sp810->timerclken[i].hw.init = &init;
+
+ sp810->timerclken[i].clk = clk_register(NULL,
+ &sp810->timerclken[i].hw);
+ WARN_ON(IS_ERR(sp810->timerclken[i].clk));
+ }
+
+ of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
+}
+CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);
diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c
new file mode 100644
index 000000000..8b8798bb9
--- /dev/null
+++ b/drivers/clk/versatile/clk-vexpress-osc.c
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-osc: " fmt
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/vexpress.h>
+
+struct vexpress_osc {
+ struct vexpress_config_func *func;
+ struct clk_hw hw;
+ unsigned long rate_min;
+ unsigned long rate_max;
+};
+
+#define to_vexpress_osc(osc) container_of(osc, struct vexpress_osc, hw)
+
+static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vexpress_osc *osc = to_vexpress_osc(hw);
+ u32 rate;
+
+ vexpress_config_read(osc->func, 0, &rate);
+
+ return rate;
+}
+
+static long vexpress_osc_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct vexpress_osc *osc = to_vexpress_osc(hw);
+
+ if (WARN_ON(osc->rate_min && rate < osc->rate_min))
+ rate = osc->rate_min;
+
+ if (WARN_ON(osc->rate_max && rate > osc->rate_max))
+ rate = osc->rate_max;
+
+ return rate;
+}
+
+static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vexpress_osc *osc = to_vexpress_osc(hw);
+
+ return vexpress_config_write(osc->func, 0, rate);
+}
+
+static struct clk_ops vexpress_osc_ops = {
+ .recalc_rate = vexpress_osc_recalc_rate,
+ .round_rate = vexpress_osc_round_rate,
+ .set_rate = vexpress_osc_set_rate,
+};
+
+
+struct clk * __init vexpress_osc_setup(struct device *dev)
+{
+ struct clk_init_data init;
+ struct vexpress_osc *osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+
+ if (!osc)
+ return NULL;
+
+ osc->func = vexpress_config_func_get_by_dev(dev);
+ if (!osc->func) {
+ kfree(osc);
+ return NULL;
+ }
+
+ init.name = dev_name(dev);
+ init.ops = &vexpress_osc_ops;
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+ osc->hw.init = &init;
+
+ return clk_register(NULL, &osc->hw);
+}
+
+void __init vexpress_osc_of_setup(struct device_node *node)
+{
+ struct clk_init_data init;
+ struct vexpress_osc *osc;
+ struct clk *clk;
+ u32 range[2];
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return;
+
+ osc->func = vexpress_config_func_get_by_node(node);
+ if (!osc->func) {
+ pr_err("Failed to obtain config func for node '%s'!\n",
+ node->name);
+ goto error;
+ }
+
+ if (of_property_read_u32_array(node, "freq-range", range,
+ ARRAY_SIZE(range)) == 0) {
+ osc->rate_min = range[0];
+ osc->rate_max = range[1];
+ }
+
+ of_property_read_string(node, "clock-output-names", &init.name);
+ if (!init.name)
+ init.name = node->name;
+
+ init.ops = &vexpress_osc_ops;
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+
+ osc->hw.init = &init;
+
+ clk = clk_register(NULL, &osc->hw);
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clock '%s'!\n", init.name);
+ goto error;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ pr_debug("Registered clock '%s'\n", init.name);
+
+ return;
+
+error:
+ if (osc->func)
+ vexpress_config_func_put(osc->func);
+ kfree(osc);
+}
+CLK_OF_DECLARE(vexpress_soc, "arm,vexpress-osc", vexpress_osc_of_setup);
diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c
new file mode 100644
index 000000000..a4a728d05
--- /dev/null
+++ b/drivers/clk/versatile/clk-vexpress.c
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#include <linux/amba/sp810.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/vexpress.h>
+
+static struct clk *vexpress_sp810_timerclken[4];
+static DEFINE_SPINLOCK(vexpress_sp810_lock);
+
+static void __init vexpress_sp810_init(void __iomem *base)
+{
+ int i;
+
+ if (WARN_ON(!base))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++) {
+ char name[12];
+ const char *parents[] = {
+ "v2m:refclk32khz", /* REFCLK */
+ "v2m:refclk1mhz" /* TIMCLK */
+ };
+
+ snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+
+ vexpress_sp810_timerclken[i] = clk_register_mux(NULL, name,
+ parents, 2, 0, base + SCCTRL,
+ SCCTRL_TIMERENnSEL_SHIFT(i), 1,
+ 0, &vexpress_sp810_lock);
+
+ if (WARN_ON(IS_ERR(vexpress_sp810_timerclken[i])))
+ break;
+ }
+}
+
+
+static const char * const vexpress_clk_24mhz_periphs[] __initconst = {
+ "mb:uart0", "mb:uart1", "mb:uart2", "mb:uart3",
+ "mb:mmci", "mb:kmi0", "mb:kmi1"
+};
+
+void __init vexpress_clk_init(void __iomem *sp810_base)
+{
+ struct clk *clk;
+ int i;
+
+ clk = clk_register_fixed_rate(NULL, "dummy_apb_pclk", NULL,
+ CLK_IS_ROOT, 0);
+ WARN_ON(clk_register_clkdev(clk, "apb_pclk", NULL));
+
+ clk = clk_register_fixed_rate(NULL, "v2m:clk_24mhz", NULL,
+ CLK_IS_ROOT, 24000000);
+ for (i = 0; i < ARRAY_SIZE(vexpress_clk_24mhz_periphs); i++)
+ WARN_ON(clk_register_clkdev(clk, NULL,
+ vexpress_clk_24mhz_periphs[i]));
+
+ clk = clk_register_fixed_rate(NULL, "v2m:refclk32khz", NULL,
+ CLK_IS_ROOT, 32768);
+ WARN_ON(clk_register_clkdev(clk, NULL, "v2m:wdt"));
+
+ clk = clk_register_fixed_rate(NULL, "v2m:refclk1mhz", NULL,
+ CLK_IS_ROOT, 1000000);
+
+ vexpress_sp810_init(sp810_base);
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++)
+ WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i], clk));
+
+ WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0],
+ "v2m-timer0", "sp804"));
+ WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
+ "v2m-timer1", "sp804"));
+}
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
new file mode 100644
index 000000000..04781389d
--- /dev/null
+++ b/drivers/clk/x86/Makefile
@@ -0,0 +1,2 @@
+clk-x86-lpss-objs := clk-lpt.o
+obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c
new file mode 100644
index 000000000..4f45eee9e
--- /dev/null
+++ b/drivers/clk/x86/clk-lpt.c
@@ -0,0 +1,55 @@
+/*
+ * Intel Lynxpoint LPSS clocks.
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Heikki Krogerus <heikki.krogerus@linux.intel.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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_data/clk-lpss.h>
+#include <linux/platform_device.h>
+
+#define PRV_CLOCK_PARAMS 0x800
+
+static int lpt_clk_probe(struct platform_device *pdev)
+{
+ struct lpss_clk_data *drvdata;
+ struct clk *clk;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ /* LPSS free running clock */
+ drvdata->name = "lpss_clk";
+ clk = clk_register_fixed_rate(&pdev->dev, drvdata->name, NULL,
+ CLK_IS_ROOT, 100000000);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ drvdata->clk = clk;
+ platform_set_drvdata(pdev, drvdata);
+ return 0;
+}
+
+static struct platform_driver lpt_clk_driver = {
+ .driver = {
+ .name = "clk-lpt",
+ .owner = THIS_MODULE,
+ },
+ .probe = lpt_clk_probe,
+};
+
+int __init lpt_clk_init(void)
+{
+ return platform_driver_register(&lpt_clk_driver);
+}