summaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-15 07:05:03 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-15 07:05:03 +0200
commitc0fa2373f8cfed90437d8d7b17e0b1a84009a10a (patch)
tree43fb2edd0c11874d0b2e56714e53894d10321e19 /drivers/clk
parentfcc3a5d277571bc6048e7b4ef8cd391b935de629 (diff)
parent98d147f50eb0ce4328e013f5f2c076896003c761 (diff)
downloadblackbird-op-linux-c0fa2373f8cfed90437d8d7b17e0b1a84009a10a.tar.gz
blackbird-op-linux-c0fa2373f8cfed90437d8d7b17e0b1a84009a10a.zip
Merge tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux
Pull clock tree updates from Mike Turquette: "The clk tree changes for 3.18 are dominated by clock drivers. Mostly fixes and enhancements to existing drivers as well as new drivers. This tag contains a bit more arch code than I usually take due to some OMAP2+ changes. Additionally it contains the restart notifier handlers which are merged as a dependency into several trees. The PXA changes are the only messy part. Due to having a stable tree I had to revert one patch and follow up with one more fix near the tip of this tag. Some dead code is introduced but it will soon become live code after 3.18-rc1 is released as the rest of the PXA family is converted over to the common clock framework. Another trend in this tag is that multiple vendors have started to push the complexity of changing their CPU frequency into the clock driver, whereas this used to be done in CPUfreq drivers. Changes to the clk core include a generic gpio-clock type and a clk_set_phase() function added to the top-level clk.h api. Due to some confusion on the fbdev mailing list the kernel boot parameters documentation was updated to further explain the clk_ignore_unused parameter, which is often required by users of the simplefb driver. Finally some fixes to the locking around the clock debugfs stuff was done to prevent deadlocks when interacting with other subsystems." * tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux: (99 commits) clk: pxa clocks build system fix Revert "arm: pxa: Transition pxa27x to clk framework" clk: samsung: register restart handlers for s3c2412 and s3c2443 clk: rockchip: add restart handler clk: rockchip: rk3288: i2s_frac adds flag to set parent's rate doc/kernel-parameters.txt: clarify clk_ignore_unused arm: pxa: Transition pxa27x to clk framework dts: add devicetree bindings for pxa27x clocks clk: add pxa27x clock drivers arm: pxa: add clock pll selection bits clk: dts: document pxa clock binding clk: add pxa clocks infrastructure clk: gpio-gate: Ensure gpiod_ APIs are prototyped clk: ti: dra7-atl-clock: Mark the device as pm_runtime_irq_safe clk: ti: LLVMLinux: Move __init outside of type definition clk: ti: consider the fact that of_clk_get() might return an error clk: ti: dra7-atl-clock: fix a memory leak clk: ti: change clock init to use generic of_clk_init clk: hix5hd2: add I2C clocks clk: hix5hd2: add watchdog0 clocks ...
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig16
-rw-r--r--drivers/clk/Makefile4
-rw-r--r--drivers/clk/at91/clk-pll.c160
-rw-r--r--drivers/clk/at91/clk-usb.c20
-rw-r--r--drivers/clk/clk-axi-clkgen.c1
-rw-r--r--drivers/clk/clk-fractional-divider.c2
-rw-r--r--drivers/clk/clk-gate.c2
-rw-r--r--drivers/clk/clk-gpio-gate.c205
-rw-r--r--drivers/clk/clk-max-gen.c192
-rw-r--r--drivers/clk/clk-max-gen.h32
-rw-r--r--drivers/clk/clk-max77686.c184
-rw-r--r--drivers/clk/clk-max77802.c97
-rw-r--r--drivers/clk/clk-palmas.c1
-rw-r--r--drivers/clk/clk-twl6040.c1
-rw-r--r--drivers/clk/clk-wm831x.c1
-rw-r--r--drivers/clk/clk.c168
-rw-r--r--drivers/clk/hisilicon/clk-hix5hd2.c232
-rw-r--r--drivers/clk/mvebu/armada-370.c8
-rw-r--r--drivers/clk/mvebu/armada-375.c4
-rw-r--r--drivers/clk/mvebu/common.c91
-rw-r--r--drivers/clk/mvebu/common.h9
-rw-r--r--drivers/clk/mvebu/kirkwood.c102
-rw-r--r--drivers/clk/pxa/Makefile2
-rw-r--r--drivers/clk/pxa/clk-pxa.c97
-rw-r--r--drivers/clk/pxa/clk-pxa.h107
-rw-r--r--drivers/clk/pxa/clk-pxa27x.c370
-rw-r--r--drivers/clk/qcom/clk-pll.c68
-rw-r--r--drivers/clk/qcom/clk-pll.h20
-rw-r--r--drivers/clk/qcom/clk-rcg.c115
-rw-r--r--drivers/clk/qcom/clk-rcg.h6
-rw-r--r--drivers/clk/qcom/clk-rcg2.c19
-rw-r--r--drivers/clk/qcom/common.c16
-rw-r--r--drivers/clk/qcom/common.h4
-rw-r--r--drivers/clk/qcom/gcc-ipq806x.c31
-rw-r--r--drivers/clk/qcom/mmcc-apq8084.c1
-rw-r--r--drivers/clk/qcom/mmcc-msm8960.c29
-rw-r--r--drivers/clk/qcom/mmcc-msm8974.c1
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-cpu.c329
-rw-r--r--drivers/clk/rockchip/clk-pll.c63
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c171
-rw-r--r--drivers/clk/rockchip/clk-rk3288.c203
-rw-r--r--drivers/clk/rockchip/clk.c119
-rw-r--r--drivers/clk/rockchip/clk.h39
-rw-r--r--drivers/clk/samsung/clk-exynos3250.c202
-rw-r--r--drivers/clk/samsung/clk-exynos4.c18
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c2
-rw-r--r--drivers/clk/samsung/clk-s3c2410-dclk.c1
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c29
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c19
-rw-r--r--drivers/clk/shmobile/clk-rcar-gen2.c1
-rw-r--r--drivers/clk/sunxi/Makefile2
-rw-r--r--drivers/clk/sunxi/clk-factors.c101
-rw-r--r--drivers/clk/sunxi/clk-factors.h16
-rw-r--r--drivers/clk/sunxi/clk-mod0.c283
-rw-r--r--drivers/clk/sunxi/clk-sun6i-apb0-gates.c1
-rw-r--r--drivers/clk/sunxi/clk-sun6i-apb0.c1
-rw-r--r--drivers/clk/sunxi/clk-sun6i-ar100.c1
-rw-r--r--drivers/clk/sunxi/clk-sun8i-apb0.c1
-rw-r--r--drivers/clk/sunxi/clk-sun8i-mbus.c78
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c161
-rw-r--r--drivers/clk/tegra/clk-tegra124.c8
-rw-r--r--drivers/clk/tegra/clk.c9
-rw-r--r--drivers/clk/ti/clk-dra7-atl.c3
-rw-r--r--drivers/clk/ti/clk.c68
-rw-r--r--drivers/clk/ti/clockdomain.c5
-rw-r--r--drivers/clk/ti/divider.c4
-rw-r--r--drivers/clk/zynq/clkc.c30
-rw-r--r--drivers/clk/zynq/pll.c4
69 files changed, 3627 insertions, 764 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 84e0590e31dc..455fd17d938e 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -32,12 +32,23 @@ config COMMON_CLK_WM831X
source "drivers/clk/versatile/Kconfig"
+config COMMON_CLK_MAX_GEN
+ bool
+
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77686 MFD"
depends on MFD_MAX77686
+ select COMMON_CLK_MAX_GEN
---help---
This driver supports Maxim 77686 crystal oscillator clock.
+config COMMON_CLK_MAX77802
+ tristate "Clock driver for Maxim 77802 PMIC"
+ depends on MFD_MAX77686
+ select COMMON_CLK_MAX_GEN
+ ---help---
+ This driver supports Maxim 77802 crystal oscillator clock.
+
config COMMON_CLK_RK808
tristate "Clock driver for RK808"
depends on MFD_RK808
@@ -118,6 +129,11 @@ config COMMON_CLK_PALMAS
This driver supports TI Palmas devices 32KHz output KG and KG_AUDIO
using common clock framework.
+config COMMON_CLK_PXA
+ def_bool COMMON_CLK && ARCH_PXA
+ ---help---
+ Sypport for the Marvell PXA SoC.
+
source "drivers/clk/qcom/Kconfig"
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 99f53d5f8766..d5fba5bc6e1b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
+obj-$(CONFIG_COMMON_CLK) += clk-gpio-gate.o
ifeq ($(CONFIG_OF), y)
obj-$(CONFIG_COMMON_CLK) += clk-conf.o
endif
@@ -22,7 +23,9 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
+obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
@@ -49,6 +52,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index cf6ed023504c..6ec79dbc0840 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -15,6 +15,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -29,9 +30,12 @@
#define PLL_DIV(reg) ((reg) & PLL_DIV_MASK)
#define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \
(layout)->mul_mask)
+#define PLL_MUL_MIN 2
+#define PLL_MUL_MASK(layout) ((layout)->mul_mask)
+#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1)
#define PLL_ICPR_SHIFT(id) ((id) * 16)
#define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id))
-#define PLL_MAX_COUNT 0x3ff
+#define PLL_MAX_COUNT 0x3f
#define PLL_COUNT_SHIFT 8
#define PLL_OUT_SHIFT 14
#define PLL_MAX_ID 1
@@ -147,115 +151,113 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_pll *pll = to_clk_pll(hw);
- const struct clk_pll_layout *layout = pll->layout;
- struct at91_pmc *pmc = pll->pmc;
- int offset = PLL_REG(pll->id);
- u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask;
- u8 div = PLL_DIV(tmp);
- u16 mul = PLL_MUL(tmp, layout);
- if (!div || !mul)
+
+ if (!pll->div || !pll->mul)
return 0;
- return (parent_rate * (mul + 1)) / div;
+ return (parent_rate / pll->div) * (pll->mul + 1);
}
static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
unsigned long parent_rate,
u32 *div, u32 *mul,
u32 *index) {
- unsigned long maxrate;
- unsigned long minrate;
- unsigned long divrate;
- unsigned long bestdiv = 1;
- unsigned long bestmul;
- unsigned long tmpdiv;
- unsigned long roundup;
- unsigned long rounddown;
- unsigned long remainder;
- unsigned long bestremainder;
- unsigned long maxmul;
- unsigned long maxdiv;
- unsigned long mindiv;
- int i = 0;
const struct clk_pll_layout *layout = pll->layout;
const struct clk_pll_characteristics *characteristics =
pll->characteristics;
+ unsigned long bestremainder = ULONG_MAX;
+ unsigned long maxdiv, mindiv, tmpdiv;
+ long bestrate = -ERANGE;
+ unsigned long bestdiv;
+ unsigned long bestmul;
+ int i = 0;
- /* Minimum divider = 1 */
- /* Maximum multiplier = max_mul */
- maxmul = layout->mul_mask + 1;
- maxrate = (parent_rate * maxmul) / 1;
-
- /* Maximum divider = max_div */
- /* Minimum multiplier = 2 */
- maxdiv = PLL_DIV_MAX;
- minrate = (parent_rate * 2) / maxdiv;
-
+ /* Check if parent_rate is a valid input rate */
if (parent_rate < characteristics->input.min ||
- parent_rate < characteristics->input.max)
- return -ERANGE;
-
- if (parent_rate < minrate || parent_rate > maxrate)
+ parent_rate > characteristics->input.max)
return -ERANGE;
- for (i = 0; i < characteristics->num_output; i++) {
- if (parent_rate >= characteristics->output[i].min &&
- parent_rate <= characteristics->output[i].max)
- break;
- }
-
- if (i >= characteristics->num_output)
- return -ERANGE;
-
- bestmul = rate / parent_rate;
- rounddown = parent_rate % rate;
- roundup = rate - rounddown;
- bestremainder = roundup < rounddown ? roundup : rounddown;
-
- if (!bestremainder) {
- if (div)
- *div = bestdiv;
- if (mul)
- *mul = bestmul;
- if (index)
- *index = i;
- return rate;
- }
-
- maxdiv = 255 / (bestmul + 1);
- if (parent_rate / maxdiv < characteristics->input.min)
- maxdiv = parent_rate / characteristics->input.min;
- mindiv = parent_rate / characteristics->input.max;
- if (parent_rate % characteristics->input.max)
- mindiv++;
-
- for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) {
- divrate = parent_rate / tmpdiv;
-
- rounddown = rate % divrate;
- roundup = divrate - rounddown;
- remainder = roundup < rounddown ? roundup : rounddown;
-
+ /*
+ * Calculate minimum divider based on the minimum multiplier, the
+ * parent_rate and the requested rate.
+ * Should always be 2 according to the input and output characteristics
+ * of the PLL blocks.
+ */
+ mindiv = (parent_rate * PLL_MUL_MIN) / rate;
+ if (!mindiv)
+ mindiv = 1;
+
+ /*
+ * Calculate the maximum divider which is limited by PLL register
+ * layout (limited by the MUL or DIV field size).
+ */
+ maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate);
+ if (maxdiv > PLL_DIV_MAX)
+ maxdiv = PLL_DIV_MAX;
+
+ /*
+ * Iterate over the acceptable divider values to find the best
+ * divider/multiplier pair (the one that generates the closest
+ * rate to the requested one).
+ */
+ for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
+ unsigned long remainder;
+ unsigned long tmprate;
+ unsigned long tmpmul;
+
+ /*
+ * Calculate the multiplier associated with the current
+ * divider that provide the closest rate to the requested one.
+ */
+ tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv);
+ tmprate = (parent_rate / tmpdiv) * tmpmul;
+ if (tmprate > rate)
+ remainder = tmprate - rate;
+ else
+ remainder = rate - tmprate;
+
+ /*
+ * Compare the remainder with the best remainder found until
+ * now and elect a new best multiplier/divider pair if the
+ * current remainder is smaller than the best one.
+ */
if (remainder < bestremainder) {
bestremainder = remainder;
- bestmul = rate / divrate;
bestdiv = tmpdiv;
+ bestmul = tmpmul;
+ bestrate = tmprate;
}
+ /*
+ * We've found a perfect match!
+ * Stop searching now and use this multiplier/divider pair.
+ */
if (!remainder)
break;
}
- rate = (parent_rate / bestdiv) * bestmul;
+ /* We haven't found any multiplier/divider pair => return -ERANGE */
+ if (bestrate < 0)
+ return bestrate;
+
+ /* Check if bestrate is a valid output rate */
+ for (i = 0; i < characteristics->num_output; i++) {
+ if (bestrate >= characteristics->output[i].min &&
+ bestrate <= characteristics->output[i].max)
+ break;
+ }
+
+ if (i >= characteristics->num_output)
+ return -ERANGE;
if (div)
*div = bestdiv;
if (mul)
- *mul = bestmul;
+ *mul = bestmul - 1;
if (index)
*index = i;
- return rate;
+ return bestrate;
}
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 7d1d26a4bd04..24b5b020753a 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -238,16 +238,22 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+ struct clk *parent = __clk_get_parent(hw->clk);
unsigned long bestrate = 0;
int bestdiff = -1;
unsigned long tmprate;
int tmpdiff;
int i = 0;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+ unsigned long tmp_parent_rate;
+
if (!usb->divisors[i])
continue;
- tmprate = *parent_rate / usb->divisors[i];
+
+ tmp_parent_rate = rate * usb->divisors[i];
+ tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate);
+ tmprate = tmp_parent_rate / usb->divisors[i];
if (tmprate < rate)
tmpdiff = rate - tmprate;
else
@@ -256,6 +262,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
if (bestdiff < 0 || bestdiff > tmpdiff) {
bestrate = tmprate;
bestdiff = tmpdiff;
+ *parent_rate = tmp_parent_rate;
}
if (!bestdiff)
@@ -272,10 +279,13 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
int i;
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
struct at91_pmc *pmc = usb->pmc;
- unsigned long div = parent_rate / rate;
+ unsigned long div;
- if (parent_rate % rate)
+ if (!rate || parent_rate % rate)
return -EINVAL;
+
+ div = parent_rate / rate;
+
for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
if (usb->divisors[i] == div) {
tmp = pmc_read(pmc, AT91_CKGR_PLLBR) &
@@ -311,7 +321,7 @@ at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name,
init.ops = &at91rm9200_usb_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
- init.flags = 0;
+ init.flags = CLK_SET_RATE_PARENT;
usb->hw.init = &init;
usb->pmc = pmc;
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 1127ee46b802..e619285c6def 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -544,7 +544,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
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,
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index ede685ca0d20..82a59d0086cc 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -36,7 +36,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
m = (val & fd->mmask) >> fd->mshift;
n = (val & fd->nmask) >> fd->nshift;
- ret = parent_rate * m;
+ ret = (u64)parent_rate * m;
do_div(ret, n);
return ret;
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 4a58c55255bd..51fd87fb7ba6 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -45,7 +45,7 @@ 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;
+ unsigned long uninitialized_var(flags);
u32 reg;
set ^= enable;
diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c
new file mode 100644
index 000000000000..08e43224fd52
--- /dev/null
+++ b/drivers/clk/clk-gpio-gate.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Jyri Sarha <jsarha@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.
+ *
+ * Gpio gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+/**
+ * DOC: basic gpio gated clock which can be enabled and disabled
+ * with gpio output
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gpio
+ * rate - inherits rate from parent. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw)
+
+static int clk_gpio_gate_enable(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value(clk->gpiod, 1);
+
+ return 0;
+}
+
+static void clk_gpio_gate_disable(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value(clk->gpiod, 0);
+}
+
+static int clk_gpio_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ return gpiod_get_value(clk->gpiod);
+}
+
+const struct clk_ops clk_gpio_gate_ops = {
+ .enable = clk_gpio_gate_enable,
+ .disable = clk_gpio_gate_disable,
+ .is_enabled = clk_gpio_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gpio_gate_ops);
+
+/**
+ * clk_register_gpio - register a gpip 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
+ * @gpiod: gpio descriptor to gate this clock
+ */
+struct clk *clk_register_gpio_gate(struct device *dev, const char *name,
+ const char *parent_name, struct gpio_desc *gpiod,
+ unsigned long flags)
+{
+ struct clk_gpio *clk_gpio = NULL;
+ struct clk *clk = ERR_PTR(-EINVAL);
+ struct clk_init_data init = { NULL };
+ unsigned long gpio_flags;
+ int err;
+
+ if (gpiod_is_active_low(gpiod))
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ else
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ if (dev)
+ err = devm_gpio_request_one(dev, desc_to_gpio(gpiod),
+ gpio_flags, name);
+ else
+ err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name);
+
+ if (err) {
+ pr_err("%s: %s: Error requesting clock control gpio %u\n",
+ __func__, name, desc_to_gpio(gpiod));
+ return ERR_PTR(err);
+ }
+
+ if (dev)
+ clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio),
+ GFP_KERNEL);
+ else
+ clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL);
+
+ if (!clk_gpio) {
+ clk = ERR_PTR(-ENOMEM);
+ goto clk_register_gpio_gate_err;
+ }
+
+ init.name = name;
+ init.ops = &clk_gpio_gate_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ clk_gpio->gpiod = gpiod;
+ clk_gpio->hw.init = &init;
+
+ clk = clk_register(dev, &clk_gpio->hw);
+
+ if (!IS_ERR(clk))
+ return clk;
+
+ if (!dev)
+ kfree(clk_gpio);
+
+clk_register_gpio_gate_err:
+ gpiod_put(gpiod);
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_gpio_gate);
+
+#ifdef CONFIG_OF
+/**
+ * The clk_register_gpio_gate has to be delayed, because the EPROBE_DEFER
+ * can not be handled properly at of_clk_init() call time.
+ */
+
+struct clk_gpio_gate_delayed_register_data {
+ struct device_node *node;
+ struct mutex lock;
+ struct clk *clk;
+};
+
+static struct clk *of_clk_gpio_gate_delayed_register_get(
+ struct of_phandle_args *clkspec,
+ void *_data)
+{
+ struct clk_gpio_gate_delayed_register_data *data = _data;
+ struct clk *clk;
+ const char *clk_name = data->node->name;
+ const char *parent_name;
+ struct gpio_desc *gpiod;
+ int gpio;
+
+ mutex_lock(&data->lock);
+
+ if (data->clk) {
+ mutex_unlock(&data->lock);
+ return data->clk;
+ }
+
+ gpio = of_get_named_gpio_flags(data->node, "enable-gpios", 0, NULL);
+ if (gpio < 0) {
+ mutex_unlock(&data->lock);
+ if (gpio != -EPROBE_DEFER)
+ pr_err("%s: %s: Can't get 'enable-gpios' DT property\n",
+ __func__, clk_name);
+ return ERR_PTR(gpio);
+ }
+ gpiod = gpio_to_desc(gpio);
+
+ parent_name = of_clk_get_parent_name(data->node, 0);
+
+ clk = clk_register_gpio_gate(NULL, clk_name, parent_name, gpiod, 0);
+ if (IS_ERR(clk)) {
+ mutex_unlock(&data->lock);
+ return clk;
+ }
+
+ data->clk = clk;
+ mutex_unlock(&data->lock);
+
+ return clk;
+}
+
+/**
+ * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock
+ */
+void __init of_gpio_gate_clk_setup(struct device_node *node)
+{
+ struct clk_gpio_gate_delayed_register_data *data;
+
+ data = kzalloc(sizeof(struct clk_gpio_gate_delayed_register_data),
+ GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->node = node;
+ mutex_init(&data->lock);
+
+ of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data);
+}
+EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup);
+CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup);
+#endif
diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c
new file mode 100644
index 000000000000..6505049d50f1
--- /dev/null
+++ b/drivers/clk/clk-max-gen.c
@@ -0,0 +1,192 @@
+/*
+ * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * 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.
+ *
+ * This driver is based on clk-max77686.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/export.h>
+
+struct max_gen_clk {
+ struct regmap *regmap;
+ u32 mask;
+ u32 reg;
+ struct clk_hw hw;
+};
+
+static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct max_gen_clk, hw);
+}
+
+static int max_gen_clk_prepare(struct clk_hw *hw)
+{
+ struct max_gen_clk *max_gen = to_max_gen_clk(hw);
+
+ return regmap_update_bits(max_gen->regmap, max_gen->reg,
+ max_gen->mask, max_gen->mask);
+}
+
+static void max_gen_clk_unprepare(struct clk_hw *hw)
+{
+ struct max_gen_clk *max_gen = to_max_gen_clk(hw);
+
+ regmap_update_bits(max_gen->regmap, max_gen->reg,
+ max_gen->mask, ~max_gen->mask);
+}
+
+static int max_gen_clk_is_prepared(struct clk_hw *hw)
+{
+ struct max_gen_clk *max_gen = to_max_gen_clk(hw);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(max_gen->regmap, max_gen->reg, &val);
+
+ if (ret < 0)
+ return -EINVAL;
+
+ return val & max_gen->mask;
+}
+
+static unsigned long max_gen_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 32768;
+}
+
+struct clk_ops max_gen_clk_ops = {
+ .prepare = max_gen_clk_prepare,
+ .unprepare = max_gen_clk_unprepare,
+ .is_prepared = max_gen_clk_is_prepared,
+ .recalc_rate = max_gen_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(max_gen_clk_ops);
+
+static struct clk *max_gen_clk_register(struct device *dev,
+ struct max_gen_clk *max_gen)
+{
+ struct clk *clk;
+ struct clk_hw *hw = &max_gen->hw;
+ int ret;
+
+ clk = devm_clk_register(dev, hw);
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = clk_register_clkdev(clk, hw->init->name, NULL);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ return clk;
+}
+
+int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap,
+ u32 reg, struct clk_init_data *clks_init, int num_init)
+{
+ int i, ret;
+ struct max_gen_clk *max_gen_clks;
+ struct clk **clocks;
+ struct device *dev = pdev->dev.parent;
+ const char *clk_name;
+ struct clk_init_data *init;
+
+ clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL);
+ if (!clocks)
+ return -ENOMEM;
+
+ max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk)
+ * num_init, GFP_KERNEL);
+ if (!max_gen_clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num_init; i++) {
+ max_gen_clks[i].regmap = regmap;
+ max_gen_clks[i].mask = 1 << i;
+ max_gen_clks[i].reg = reg;
+
+ init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL);
+ if (!init)
+ return -ENOMEM;
+
+ if (dev->of_node &&
+ !of_property_read_string_index(dev->of_node,
+ "clock-output-names",
+ i, &clk_name))
+ init->name = clk_name;
+ else
+ init->name = clks_init[i].name;
+
+ init->ops = clks_init[i].ops;
+ init->flags = clks_init[i].flags;
+
+ max_gen_clks[i].hw.init = init;
+
+ clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]);
+ if (IS_ERR(clocks[i])) {
+ ret = PTR_ERR(clocks[i]);
+ dev_err(dev, "failed to register %s\n",
+ max_gen_clks[i].hw.init->name);
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, clocks);
+
+ if (dev->of_node) {
+ struct clk_onecell_data *of_data;
+
+ of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL);
+ if (!of_data)
+ return -ENOMEM;
+
+ of_data->clks = clocks;
+ of_data->clk_num = num_init;
+ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+ of_data);
+
+ if (ret) {
+ dev_err(dev, "failed to register OF clock provider\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max_gen_clk_probe);
+
+int max_gen_clk_remove(struct platform_device *pdev, int num_init)
+{
+ struct device *dev = pdev->dev.parent;
+
+ if (dev->of_node)
+ of_clk_del_provider(dev->of_node);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max_gen_clk_remove);
diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h
new file mode 100644
index 000000000000..997e86fc3f4d
--- /dev/null
+++ b/drivers/clk/clk-max-gen.h
@@ -0,0 +1,32 @@
+/*
+ * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __CLK_MAX_GEN_H__
+#define __CLK_MAX_GEN_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clkdev.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+
+int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap,
+ u32 reg, struct clk_init_data *clks_init, int num_init);
+int max_gen_clk_remove(struct platform_device *pdev, int num_init);
+extern struct clk_ops max_gen_clk_ops;
+
+#endif /* __CLK_MAX_GEN_H__ */
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
index 3d7e8dd8fd58..86cdb3a28629 100644
--- a/drivers/clk/clk-max77686.c
+++ b/drivers/clk/clk-max77686.c
@@ -30,193 +30,38 @@
#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_prepared(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 unsigned long max77686_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- return 32768;
-}
-
-static struct clk_ops max77686_clk_ops = {
- .prepare = max77686_clk_prepare,
- .unprepare = max77686_clk_unprepare,
- .is_prepared = max77686_clk_is_prepared,
- .recalc_rate = max77686_recalc_rate,
-};
+#include <dt-bindings/clock/maxim,max77686.h>
+#include "clk-max-gen.h"
static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
[MAX77686_CLK_AP] = {
.name = "32khz_ap",
- .ops = &max77686_clk_ops,
+ .ops = &max_gen_clk_ops,
.flags = CLK_IS_ROOT,
},
[MAX77686_CLK_CP] = {
.name = "32khz_cp",
- .ops = &max77686_clk_ops,
+ .ops = &max_gen_clk_ops,
.flags = CLK_IS_ROOT,
},
[MAX77686_CLK_PMIC] = {
.name = "32khz_pmic",
- .ops = &max77686_clk_ops,
+ .ops = &max_gen_clk_ops,
.flags = CLK_IS_ROOT,
},
};
-static struct clk *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 clk;
-
- max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
- if (!max77686->lookup)
- return ERR_PTR(-ENOMEM);
-
- max77686->lookup->con_id = hw->init->name;
- max77686->lookup->clk = clk;
-
- clkdev_add(max77686->lookup);
-
- return clk;
-}
-
static int max77686_clk_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
- struct clk **clocks;
- int i, ret;
-
- clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
- * MAX77686_CLKS_NUM, GFP_KERNEL);
- if (!clocks)
- 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];
-
- clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]);
- if (IS_ERR(clocks[i])) {
- ret = PTR_ERR(clocks[i]);
- dev_err(&pdev->dev, "failed to register %s\n",
- max77686_clks[i]->hw.init->name);
- goto err_clocks;
- }
- }
-
- platform_set_drvdata(pdev, clocks);
-
- if (iodev->dev->of_node) {
- struct clk_onecell_data *of_data;
- of_data = devm_kzalloc(&pdev->dev,
- sizeof(*of_data), GFP_KERNEL);
- if (!of_data) {
- ret = -ENOMEM;
- goto err_clocks;
- }
-
- of_data->clks = clocks;
- of_data->clk_num = MAX77686_CLKS_NUM;
- ret = of_clk_add_provider(iodev->dev->of_node,
- of_clk_src_onecell_get, of_data);
- if (ret) {
- dev_err(&pdev->dev, "failed to register OF clock provider\n");
- goto err_clocks;
- }
- }
-
- return 0;
-
-err_clocks:
- for (--i; i >= 0; --i) {
- clkdev_drop(max77686_clks[i]->lookup);
- clk_unregister(max77686_clks[i]->hw.clk);
- }
-
- return ret;
+ return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ,
+ max77686_clks_init, MAX77686_CLKS_NUM);
}
static int max77686_clk_remove(struct platform_device *pdev)
{
- struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct clk **clocks = platform_get_drvdata(pdev);
- int i;
-
- if (iodev->dev->of_node)
- of_clk_del_provider(iodev->dev->of_node);
-
- for (i = 0; i < MAX77686_CLKS_NUM; i++) {
- struct clk_hw *hw = __clk_get_hw(clocks[i]);
- struct max77686_clk *max77686 = to_max77686_clk(hw);
-
- clkdev_drop(max77686->lookup);
- clk_unregister(clocks[i]);
- }
- return 0;
+ return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM);
}
static const struct platform_device_id max77686_clk_id[] = {
@@ -228,24 +73,13 @@ 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_platform_driver(max77686_clk_driver);
MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c
new file mode 100644
index 000000000000..0729dc723a8f
--- /dev/null
+++ b/drivers/clk/clk-max77802.c
@@ -0,0 +1,97 @@
+/*
+ * clk-max77802.c - Clock driver for Maxim 77802
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * 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.
+ *
+ * This driver is based on clk-max77686.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+#include <dt-bindings/clock/maxim,max77802.h>
+#include "clk-max-gen.h"
+
+#define MAX77802_CLOCK_OPMODE_MASK 0x1
+#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
+
+static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = {
+ [MAX77802_CLK_32K_AP] = {
+ .name = "32khz_ap",
+ .ops = &max_gen_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [MAX77802_CLK_32K_CP] = {
+ .name = "32khz_cp",
+ .ops = &max_gen_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+};
+
+static int max77802_clk_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ,
+ max77802_clks_init, MAX77802_CLKS_NUM);
+
+ if (ret) {
+ dev_err(&pdev->dev, "generic probe failed %d\n", ret);
+ return ret;
+ }
+
+ /* Enable low-jitter mode on the 32khz clocks. */
+ ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ,
+ 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
+ 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to enable low-jitter mode\n");
+
+ return ret;
+}
+
+static int max77802_clk_remove(struct platform_device *pdev)
+{
+ return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM);
+}
+
+static const struct platform_device_id max77802_clk_id[] = {
+ { "max77802-clk", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, max77802_clk_id);
+
+static struct platform_driver max77802_clk_driver = {
+ .driver = {
+ .name = "max77802-clk",
+ },
+ .probe = max77802_clk_probe,
+ .remove = max77802_clk_remove,
+ .id_table = max77802_clk_id,
+};
+
+module_platform_driver(max77802_clk_driver);
+
+MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
+MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
index 781630e1372b..8d459923a15f 100644
--- a/drivers/clk/clk-palmas.c
+++ b/drivers/clk/clk-palmas.c
@@ -292,7 +292,6 @@ static int palmas_clks_remove(struct platform_device *pdev)
static struct platform_driver palmas_clks_driver = {
.driver = {
.name = "palmas-clk",
- .owner = THIS_MODULE,
.of_match_table = palmas_clks_of_match,
},
.probe = palmas_clks_probe,
diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
index 1ada79a28052..4a755135bcd3 100644
--- a/drivers/clk/clk-twl6040.c
+++ b/drivers/clk/clk-twl6040.c
@@ -112,7 +112,6 @@ static int twl6040_clk_remove(struct platform_device *pdev)
static struct platform_driver twl6040_clk_driver = {
.driver = {
.name = "twl6040-clk",
- .owner = THIS_MODULE,
},
.probe = twl6040_clk_probe,
.remove = twl6040_clk_remove,
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index b131041c8f48..ef67719f4e52 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -395,7 +395,6 @@ static struct platform_driver wm831x_clk_driver = {
.probe = wm831x_clk_probe,
.driver = {
.name = "wm831x-clk",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index bacc06ff939b..4896ae9e23da 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -100,6 +100,8 @@ static void clk_enable_unlock(unsigned long flags)
static struct dentry *rootdir;
static int inited = 0;
+static DEFINE_MUTEX(clk_debug_lock);
+static HLIST_HEAD(clk_debug_list);
static struct hlist_head *all_lists[] = {
&clk_root_list,
@@ -117,11 +119,11 @@ 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 %11lu %10lu\n",
+ seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n",
level * 3 + 1, "",
30 - level * 3, c->name,
c->enable_count, c->prepare_count, clk_get_rate(c),
- clk_get_accuracy(c));
+ clk_get_accuracy(c), clk_get_phase(c));
}
static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
@@ -143,8 +145,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
struct clk *c;
struct hlist_head **lists = (struct hlist_head **)s->private;
- seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n");
- seq_puts(s, "--------------------------------------------------------------------------------\n");
+ seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n");
+ seq_puts(s, "----------------------------------------------------------------------------------------\n");
clk_prepare_lock();
@@ -180,6 +182,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
seq_printf(s, "\"rate\": %lu", clk_get_rate(c));
seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c));
+ seq_printf(s, "\"phase\": %d", clk_get_phase(c));
}
static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
@@ -264,6 +267,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
if (!d)
goto err_out;
+ d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry,
+ (u32 *)&clk->phase);
+ if (!d)
+ goto err_out;
+
d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
(u32 *)&clk->flags);
if (!d)
@@ -300,28 +308,6 @@ 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, pdentry);
-
- 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
@@ -329,20 +315,21 @@ out:
* 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)
{
int ret = 0;
+ mutex_lock(&clk_debug_lock);
+ hlist_add_head(&clk->debug_node, &clk_debug_list);
+
if (!inited)
- goto out;
+ goto unlock;
- ret = clk_debug_create_subtree(clk, rootdir);
+ ret = clk_debug_create_one(clk, rootdir);
+unlock:
+ mutex_unlock(&clk_debug_lock);
-out:
return ret;
}
@@ -353,12 +340,18 @@ out:
* Dynamically removes a clk and all it's children clk nodes from the
* debugfs clk tree if clk->dentry points to debugfs created by
* clk_debug_register in __clk_init.
- *
- * Caller must hold prepare_lock.
*/
static void clk_debug_unregister(struct clk *clk)
{
+ mutex_lock(&clk_debug_lock);
+ if (!clk->dentry)
+ goto out;
+
+ hlist_del_init(&clk->debug_node);
debugfs_remove_recursive(clk->dentry);
+ clk->dentry = NULL;
+out:
+ mutex_unlock(&clk_debug_lock);
}
struct dentry *clk_debugfs_add_file(struct clk *clk, char *name, umode_t mode,
@@ -415,17 +408,12 @@ static int __init clk_debug_init(void)
if (!d)
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, rootdir);
+ mutex_lock(&clk_debug_lock);
+ hlist_for_each_entry(clk, &clk_debug_list, debug_node)
+ clk_debug_create_one(clk, rootdir);
inited = 1;
-
- clk_prepare_unlock();
+ mutex_unlock(&clk_debug_lock);
return 0;
}
@@ -1744,6 +1732,77 @@ out:
EXPORT_SYMBOL_GPL(clk_set_parent);
/**
+ * clk_set_phase - adjust the phase shift of a clock signal
+ * @clk: clock signal source
+ * @degrees: number of degrees the signal is shifted
+ *
+ * Shifts the phase of a clock signal by the specified
+ * degrees. Returns 0 on success, -EERROR otherwise.
+ *
+ * This function makes no distinction about the input or reference
+ * signal that we adjust the clock signal phase against. For example
+ * phase locked-loop clock signal generators we may shift phase with
+ * respect to feedback clock signal input, but for other cases the
+ * clock phase may be shifted with respect to some other, unspecified
+ * signal.
+ *
+ * Additionally the concept of phase shift does not propagate through
+ * the clock tree hierarchy, which sets it apart from clock rates and
+ * clock accuracy. A parent clock phase attribute does not have an
+ * impact on the phase attribute of a child clock.
+ */
+int clk_set_phase(struct clk *clk, int degrees)
+{
+ int ret = 0;
+
+ if (!clk)
+ goto out;
+
+ /* sanity check degrees */
+ degrees %= 360;
+ if (degrees < 0)
+ degrees += 360;
+
+ clk_prepare_lock();
+
+ if (!clk->ops->set_phase)
+ goto out_unlock;
+
+ ret = clk->ops->set_phase(clk->hw, degrees);
+
+ if (!ret)
+ clk->phase = degrees;
+
+out_unlock:
+ clk_prepare_unlock();
+
+out:
+ return ret;
+}
+
+/**
+ * clk_get_phase - return the phase shift of a clock signal
+ * @clk: clock signal source
+ *
+ * Returns the phase shift of a clock node in degrees, otherwise returns
+ * -EERROR.
+ */
+int clk_get_phase(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ goto out;
+
+ clk_prepare_lock();
+ ret = clk->phase;
+ clk_prepare_unlock();
+
+out:
+ return ret;
+}
+
+/**
* __clk_init - initialize the data structures in a struct clk
* @dev: device initializing this clk, placeholder for now
* @clk: clk being initialized
@@ -1862,6 +1921,16 @@ int __clk_init(struct device *dev, struct clk *clk)
clk->accuracy = 0;
/*
+ * Set clk's phase.
+ * Since a phase is by definition relative to its parent, just
+ * query the current clock phase, or just assume it's in phase.
+ */
+ if (clk->ops->get_phase)
+ clk->phase = clk->ops->get_phase(clk->hw);
+ else
+ clk->phase = 0;
+
+ /*
* 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)
@@ -2092,14 +2161,16 @@ void clk_unregister(struct clk *clk)
{
unsigned long flags;
- if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
- return;
+ if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
+ return;
+
+ clk_debug_unregister(clk);
clk_prepare_lock();
if (clk->ops == &clk_nodrv_ops) {
pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
- goto out;
+ return;
}
/*
* Assign empty clock ops for consumers that might still hold
@@ -2118,16 +2189,13 @@ void clk_unregister(struct clk *clk)
clk_set_parent(child, NULL);
}
- clk_debug_unregister(clk);
-
hlist_del_init(&clk->child_node);
if (clk->prepare_count)
pr_warn("%s: unregistering prepared clock: %s\n",
__func__, clk->name);
-
kref_put(&clk->ref, __clk_release);
-out:
+
clk_prepare_unlock();
}
EXPORT_SYMBOL_GPL(clk_unregister);
diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c
index e5fcfb4e32ef..3f369c60fe56 100644
--- a/drivers/clk/hisilicon/clk-hix5hd2.c
+++ b/drivers/clk/hisilicon/clk-hix5hd2.c
@@ -9,6 +9,8 @@
#include <linux/of_address.h>
#include <dt-bindings/clock/hix5hd2-clock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
#include "clk.h"
static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = {
@@ -48,9 +50,9 @@ static const char *sfc_mux_p[] __initconst = {
"24m", "150m", "200m", "100m", "75m", };
static u32 sfc_mux_table[] = {0, 4, 5, 6, 7};
-static const char *sdio1_mux_p[] __initconst = {
+static const char *sdio_mux_p[] __initconst = {
"75m", "100m", "50m", "15m", };
-static u32 sdio1_mux_table[] = {0, 1, 2, 3};
+static u32 sdio_mux_table[] = {0, 1, 2, 3};
static const char *fephy_mux_p[] __initconst = { "25m", "125m"};
static u32 fephy_mux_table[] = {0, 1};
@@ -59,28 +61,243 @@ static u32 fephy_mux_table[] = {0, 1};
static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = {
{ HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p),
CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, },
- { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p),
- CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, },
+ { HIX5HD2_MMC_MUX, "mmc_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p),
+ CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio_mux_table, },
+ { HIX5HD2_SD_MUX, "sd_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p),
+ CLK_SET_RATE_PARENT, 0x9c, 8, 2, 0, sdio_mux_table, },
{ HIX5HD2_FEPHY_MUX, "fephy_mux",
fephy_mux_p, ARRAY_SIZE(fephy_mux_p),
CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, },
};
static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = {
- /*sfc*/
+ /* sfc */
{ HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux",
CLK_SET_RATE_PARENT, 0x5c, 0, 0, },
{ HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc",
CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, },
- /*sdio1*/
+ /* sdio0 */
+ { HIX5HD2_SD_BIU_CLK, "clk_sd_biu", "200m",
+ CLK_SET_RATE_PARENT, 0x9c, 0, 0, },
+ { HIX5HD2_SD_CIU_CLK, "clk_sd_ciu", "sd_mux",
+ CLK_SET_RATE_PARENT, 0x9c, 1, 0, },
+ { HIX5HD2_SD_CIU_RST, "rst_sd_ciu", "clk_sd_ciu",
+ CLK_SET_RATE_PARENT, 0x9c, 4, CLK_GATE_SET_TO_DISABLE, },
+ /* sdio1 */
{ HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m",
CLK_SET_RATE_PARENT, 0xa0, 0, 0, },
{ HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux",
CLK_SET_RATE_PARENT, 0xa0, 1, 0, },
{ HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu",
CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, },
+ /* gsf */
+ { HIX5HD2_FWD_BUS_CLK, "clk_fwd_bus", NULL, 0, 0xcc, 0, 0, },
+ { HIX5HD2_FWD_SYS_CLK, "clk_fwd_sys", "clk_fwd_bus", 0, 0xcc, 5, 0, },
+ { HIX5HD2_MAC0_PHY_CLK, "clk_fephy", "clk_fwd_sys",
+ CLK_SET_RATE_PARENT, 0x120, 0, 0, },
+ /* wdg0 */
+ { HIX5HD2_WDG0_CLK, "clk_wdg0", "24m",
+ CLK_SET_RATE_PARENT, 0x178, 0, 0, },
+ { HIX5HD2_WDG0_RST, "rst_wdg0", "clk_wdg0",
+ CLK_SET_RATE_PARENT, 0x178, 4, CLK_GATE_SET_TO_DISABLE, },
+ /* I2C */
+ {HIX5HD2_I2C0_CLK, "clk_i2c0", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 4, 0, },
+ {HIX5HD2_I2C0_RST, "rst_i2c0", "clk_i2c0",
+ CLK_SET_RATE_PARENT, 0x06c, 5, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C1_CLK, "clk_i2c1", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 8, 0, },
+ {HIX5HD2_I2C1_RST, "rst_i2c1", "clk_i2c1",
+ CLK_SET_RATE_PARENT, 0x06c, 9, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C2_CLK, "clk_i2c2", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 12, 0, },
+ {HIX5HD2_I2C2_RST, "rst_i2c2", "clk_i2c2",
+ CLK_SET_RATE_PARENT, 0x06c, 13, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C3_CLK, "clk_i2c3", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 16, 0, },
+ {HIX5HD2_I2C3_RST, "rst_i2c3", "clk_i2c3",
+ CLK_SET_RATE_PARENT, 0x06c, 17, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C4_CLK, "clk_i2c4", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 20, 0, },
+ {HIX5HD2_I2C4_RST, "rst_i2c4", "clk_i2c4",
+ CLK_SET_RATE_PARENT, 0x06c, 21, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C5_CLK, "clk_i2c5", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 0, 0, },
+ {HIX5HD2_I2C5_RST, "rst_i2c5", "clk_i2c5",
+ CLK_SET_RATE_PARENT, 0x06c, 1, CLK_GATE_SET_TO_DISABLE, },
};
+enum hix5hd2_clk_type {
+ TYPE_COMPLEX,
+ TYPE_ETHER,
+};
+
+struct hix5hd2_complex_clock {
+ const char *name;
+ const char *parent_name;
+ u32 id;
+ u32 ctrl_reg;
+ u32 ctrl_clk_mask;
+ u32 ctrl_rst_mask;
+ u32 phy_reg;
+ u32 phy_clk_mask;
+ u32 phy_rst_mask;
+ enum hix5hd2_clk_type type;
+};
+
+struct hix5hd2_clk_complex {
+ struct clk_hw hw;
+ u32 id;
+ void __iomem *ctrl_reg;
+ u32 ctrl_clk_mask;
+ u32 ctrl_rst_mask;
+ void __iomem *phy_reg;
+ u32 phy_clk_mask;
+ u32 phy_rst_mask;
+};
+
+static struct hix5hd2_complex_clock hix5hd2_complex_clks[] __initdata = {
+ {"clk_mac0", "clk_fephy", HIX5HD2_MAC0_CLK,
+ 0xcc, 0xa, 0x500, 0x120, 0, 0x10, TYPE_ETHER},
+ {"clk_mac1", "clk_fwd_sys", HIX5HD2_MAC1_CLK,
+ 0xcc, 0x14, 0xa00, 0x168, 0x2, 0, TYPE_ETHER},
+ {"clk_sata", NULL, HIX5HD2_SATA_CLK,
+ 0xa8, 0x1f, 0x300, 0xac, 0x1, 0x0, TYPE_COMPLEX},
+ {"clk_usb", NULL, HIX5HD2_USB_CLK,
+ 0xb8, 0xff, 0x3f000, 0xbc, 0x7, 0x3f00, TYPE_COMPLEX},
+};
+
+#define to_complex_clk(_hw) container_of(_hw, struct hix5hd2_clk_complex, hw)
+
+static int clk_ether_prepare(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val |= clk->ctrl_clk_mask | clk->ctrl_rst_mask;
+ writel_relaxed(val, clk->ctrl_reg);
+ val &= ~(clk->ctrl_rst_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+
+ val = readl_relaxed(clk->phy_reg);
+ val |= clk->phy_clk_mask;
+ val &= ~(clk->phy_rst_mask);
+ writel_relaxed(val, clk->phy_reg);
+ mdelay(10);
+
+ val &= ~(clk->phy_clk_mask);
+ val |= clk->phy_rst_mask;
+ writel_relaxed(val, clk->phy_reg);
+ mdelay(10);
+
+ val |= clk->phy_clk_mask;
+ val &= ~(clk->phy_rst_mask);
+ writel_relaxed(val, clk->phy_reg);
+ mdelay(30);
+ return 0;
+}
+
+static void clk_ether_unprepare(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val &= ~(clk->ctrl_clk_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+}
+
+static struct clk_ops clk_ether_ops = {
+ .prepare = clk_ether_prepare,
+ .unprepare = clk_ether_unprepare,
+};
+
+static int clk_complex_enable(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val |= clk->ctrl_clk_mask;
+ val &= ~(clk->ctrl_rst_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+
+ val = readl_relaxed(clk->phy_reg);
+ val |= clk->phy_clk_mask;
+ val &= ~(clk->phy_rst_mask);
+ writel_relaxed(val, clk->phy_reg);
+
+ return 0;
+}
+
+static void clk_complex_disable(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val |= clk->ctrl_rst_mask;
+ val &= ~(clk->ctrl_clk_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+
+ val = readl_relaxed(clk->phy_reg);
+ val |= clk->phy_rst_mask;
+ val &= ~(clk->phy_clk_mask);
+ writel_relaxed(val, clk->phy_reg);
+}
+
+static struct clk_ops clk_complex_ops = {
+ .enable = clk_complex_enable,
+ .disable = clk_complex_disable,
+};
+
+void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks,
+ int nums, struct hisi_clock_data *data)
+{
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ struct hix5hd2_clk_complex *p_clk;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL);
+ if (!p_clk)
+ return;
+
+ init.name = clks[i].name;
+ if (clks[i].type == TYPE_ETHER)
+ init.ops = &clk_ether_ops;
+ else
+ init.ops = &clk_complex_ops;
+
+ init.flags = CLK_IS_BASIC;
+ init.parent_names =
+ (clks[i].parent_name ? &clks[i].parent_name : NULL);
+ init.num_parents = (clks[i].parent_name ? 1 : 0);
+
+ p_clk->ctrl_reg = base + clks[i].ctrl_reg;
+ p_clk->ctrl_clk_mask = clks[i].ctrl_clk_mask;
+ p_clk->ctrl_rst_mask = clks[i].ctrl_rst_mask;
+ p_clk->phy_reg = base + clks[i].phy_reg;
+ p_clk->phy_clk_mask = clks[i].phy_clk_mask;
+ p_clk->phy_rst_mask = clks[i].phy_rst_mask;
+ p_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &p_clk->hw);
+ if (IS_ERR(clk)) {
+ kfree(p_clk);
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ continue;
+ }
+
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+}
+
static void __init hix5hd2_clk_init(struct device_node *np)
{
struct hisi_clock_data *clk_data;
@@ -96,6 +313,9 @@ static void __init hix5hd2_clk_init(struct device_node *np)
clk_data);
hisi_clk_register_gate(hix5hd2_gate_clks,
ARRAY_SIZE(hix5hd2_gate_clks), clk_data);
+ hix5hd2_clk_register_complex(hix5hd2_complex_clks,
+ ARRAY_SIZE(hix5hd2_complex_clks),
+ clk_data);
}
CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init);
diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c
index bef198a83863..756f0f39d6a3 100644
--- a/drivers/clk/mvebu/armada-370.c
+++ b/drivers/clk/mvebu/armada-370.c
@@ -23,6 +23,7 @@
*/
#define SARL 0 /* Low part [0:31] */
+#define SARL_A370_SSCG_ENABLE BIT(10)
#define SARL_A370_PCLK_FREQ_OPT 11
#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF
#define SARL_A370_FAB_FREQ_OPT 15
@@ -133,10 +134,17 @@ static void __init a370_get_clk_ratio(
}
}
+static bool a370_is_sscg_enabled(void __iomem *sar)
+{
+ return !(readl(sar) & SARL_A370_SSCG_ENABLE);
+}
+
static const struct coreclk_soc_desc a370_coreclks = {
.get_tclk_freq = a370_get_tclk_freq,
.get_cpu_freq = a370_get_cpu_freq,
.get_clk_ratio = a370_get_clk_ratio,
+ .is_sscg_enabled = a370_is_sscg_enabled,
+ .fix_sscg_deviation = kirkwood_fix_sscg_deviation,
.ratios = a370_coreclk_ratios,
.num_ratios = ARRAY_SIZE(a370_coreclk_ratios),
};
diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c
index c991a4d95e10..c7af2242b796 100644
--- a/drivers/clk/mvebu/armada-375.c
+++ b/drivers/clk/mvebu/armada-375.c
@@ -27,14 +27,14 @@
* all modified at the same time, and not separately as for the Armada
* 370 or the Armada XP SoCs.
*
- * SAR0[21:17] : CPU frequency DDR frequency L2 frequency
+ * SAR1[21:17] : CPU frequency DDR frequency L2 frequency
* 6 = 400 MHz 400 MHz 200 MHz
* 15 = 600 MHz 600 MHz 300 MHz
* 21 = 800 MHz 534 MHz 400 MHz
* 25 = 1000 MHz 500 MHz 500 MHz
* others reserved.
*
- * SAR0[22] : TCLK frequency
+ * SAR1[22] : TCLK frequency
* 0 = 166 MHz
* 1 = 200 MHz
*/
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 25ceccf939ad..b7fcb469c87a 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -26,8 +26,85 @@
* Core Clocks
*/
+#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
+#define SSCG_SPREAD_DOWN 0x0
+#define SSCG_SPREAD_UP 0x1
+#define SSCG_SPREAD_CENTRAL 0x2
+#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
+#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
+
static struct clk_onecell_data clk_data;
+/*
+ * This function can be used by the Kirkwood, the Armada 370, the
+ * Armada XP and the Armada 375 SoC. The name of the function was
+ * chosen following the dt convention: using the first known SoC
+ * compatible with it.
+ */
+u32 kirkwood_fix_sscg_deviation(u32 system_clk)
+{
+ struct device_node *sscg_np = NULL;
+ void __iomem *sscg_map;
+ u32 sscg_reg;
+ s32 low_bound, high_bound;
+ u64 freq_swing_half;
+
+ sscg_np = of_find_node_by_name(NULL, "sscg");
+ if (sscg_np == NULL) {
+ pr_err("cannot get SSCG register node\n");
+ return system_clk;
+ }
+
+ sscg_map = of_iomap(sscg_np, 0);
+ if (sscg_map == NULL) {
+ pr_err("cannot map SSCG register\n");
+ goto out;
+ }
+
+ sscg_reg = readl(sscg_map);
+ high_bound = SSCG_CONF_HIGH(sscg_reg);
+ low_bound = SSCG_CONF_LOW(sscg_reg);
+
+ if ((high_bound - low_bound) <= 0)
+ goto out;
+ /*
+ * From Marvell engineer we got the following formula (when
+ * this code was written, the datasheet was erroneous)
+ * Spread percentage = 1/96 * (H - L) / H
+ * H = SSCG_High_Boundary
+ * L = SSCG_Low_Boundary
+ *
+ * As the deviation is half of spread then it lead to the
+ * following formula in the code.
+ *
+ * To avoid an overflow and not lose any significant digit in
+ * the same time we have to use a 64 bit integer.
+ */
+
+ freq_swing_half = (((u64)high_bound - (u64)low_bound)
+ * (u64)system_clk);
+ do_div(freq_swing_half, (2 * 96 * high_bound));
+
+ switch (SSCG_CONF_MODE(sscg_reg)) {
+ case SSCG_SPREAD_DOWN:
+ system_clk -= freq_swing_half;
+ break;
+ case SSCG_SPREAD_UP:
+ system_clk += freq_swing_half;
+ break;
+ case SSCG_SPREAD_CENTRAL:
+ default:
+ break;
+ }
+
+ iounmap(sscg_map);
+
+out:
+ of_node_put(sscg_np);
+
+ return system_clk;
+}
+
void __init mvebu_coreclk_setup(struct device_node *np,
const struct coreclk_soc_desc *desc)
{
@@ -62,6 +139,11 @@ void __init mvebu_coreclk_setup(struct device_node *np,
of_property_read_string_index(np, "clock-output-names", 1,
&cpuclk_name);
rate = desc->get_cpu_freq(base);
+
+ if (desc->is_sscg_enabled && desc->fix_sscg_deviation
+ && desc->is_sscg_enabled(base))
+ rate = desc->fix_sscg_deviation(rate);
+
clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
CLK_IS_ROOT, rate);
WARN_ON(IS_ERR(clk_data.clks[1]));
@@ -89,8 +171,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
* Clock Gating Control
*/
+DEFINE_SPINLOCK(ctrl_gating_lock);
+
struct clk_gating_ctrl {
- spinlock_t lock;
+ spinlock_t *lock;
struct clk **gates;
int num_gates;
};
@@ -138,7 +222,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
if (WARN_ON(!ctrl))
goto ctrl_out;
- spin_lock_init(&ctrl->lock);
+ /* lock must already be initialized */
+ ctrl->lock = &ctrl_gating_lock;
/* Count, allocate, and register clock gates */
for (n = 0; desc[n].name;)
@@ -155,7 +240,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
(desc[n].parent) ? desc[n].parent : default_parent;
ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent,
desc[n].flags, base, desc[n].bit_idx,
- 0, &ctrl->lock);
+ 0, ctrl->lock);
WARN_ON(IS_ERR(ctrl->gates[n]));
}
diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
index f968b4d9df92..783b5631a453 100644
--- a/drivers/clk/mvebu/common.h
+++ b/drivers/clk/mvebu/common.h
@@ -17,6 +17,8 @@
#include <linux/kernel.h>
+extern spinlock_t ctrl_gating_lock;
+
struct device_node;
struct coreclk_ratio {
@@ -28,6 +30,8 @@ struct coreclk_soc_desc {
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);
+ bool (*is_sscg_enabled)(void __iomem *sar);
+ u32 (*fix_sscg_deviation)(u32 system_clk);
const struct coreclk_ratio *ratios;
int num_ratios;
};
@@ -45,4 +49,9 @@ void __init mvebu_coreclk_setup(struct device_node *np,
void __init mvebu_clk_gating_setup(struct device_node *np,
const struct clk_gating_soc_desc *desc);
+/*
+ * This function is shared among the Kirkwood, Armada 370, Armada XP
+ * and Armada 375 SoC
+ */
+u32 kirkwood_fix_sscg_deviation(u32 system_clk);
#endif
diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c
index ddb666a86500..99550f25975e 100644
--- a/drivers/clk/mvebu/kirkwood.c
+++ b/drivers/clk/mvebu/kirkwood.c
@@ -13,9 +13,11 @@
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include "common.h"
/*
@@ -214,7 +216,6 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = {
{ "runit", NULL, 7, 0 },
{ "xor0", NULL, 8, 0 },
{ "audio", NULL, 9, 0 },
- { "powersave", "cpuclk", 11, 0 },
{ "sata0", NULL, 14, 0 },
{ "sata1", NULL, 15, 0 },
{ "xor1", NULL, 16, 0 },
@@ -225,6 +226,101 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = {
{ }
};
+
+/*
+ * Clock Muxing Control
+ */
+
+struct clk_muxing_soc_desc {
+ const char *name;
+ const char **parents;
+ int num_parents;
+ int shift;
+ int width;
+ unsigned long flags;
+};
+
+struct clk_muxing_ctrl {
+ spinlock_t *lock;
+ struct clk **muxes;
+ int num_muxes;
+};
+
+static const char *powersave_parents[] = {
+ "cpuclk",
+ "ddrclk",
+};
+
+static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = {
+ { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents),
+ 11, 1, 0 },
+};
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static struct clk *clk_muxing_get_src(
+ struct of_phandle_args *clkspec, void *data)
+{
+ struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data;
+ int n;
+
+ if (clkspec->args_count < 1)
+ return ERR_PTR(-EINVAL);
+
+ for (n = 0; n < ctrl->num_muxes; n++) {
+ struct clk_mux *mux =
+ to_clk_mux(__clk_get_hw(ctrl->muxes[n]));
+ if (clkspec->args[0] == mux->shift)
+ return ctrl->muxes[n];
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+static void __init kirkwood_clk_muxing_setup(struct device_node *np,
+ const struct clk_muxing_soc_desc *desc)
+{
+ struct clk_muxing_ctrl *ctrl;
+ void __iomem *base;
+ int n;
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (WARN_ON(!ctrl))
+ goto ctrl_out;
+
+ /* lock must already be initialized */
+ ctrl->lock = &ctrl_gating_lock;
+
+ /* Count, allocate, and register clock muxes */
+ for (n = 0; desc[n].name;)
+ n++;
+
+ ctrl->num_muxes = n;
+ ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!ctrl->muxes))
+ goto muxes_out;
+
+ for (n = 0; n < ctrl->num_muxes; n++) {
+ ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name,
+ desc[n].parents, desc[n].num_parents,
+ desc[n].flags, base, desc[n].shift,
+ desc[n].width, desc[n].flags, ctrl->lock);
+ WARN_ON(IS_ERR(ctrl->muxes[n]));
+ }
+
+ of_clk_add_provider(np, clk_muxing_get_src, ctrl);
+
+ return;
+muxes_out:
+ kfree(ctrl);
+ctrl_out:
+ iounmap(base);
+}
+
static void __init kirkwood_clk_init(struct device_node *np)
{
struct device_node *cgnp =
@@ -236,8 +332,10 @@ static void __init kirkwood_clk_init(struct device_node *np)
else
mvebu_coreclk_setup(np, &kirkwood_coreclks);
- if (cgnp)
+ if (cgnp) {
mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc);
+ kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc);
+ }
}
CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock",
kirkwood_clk_init);
diff --git a/drivers/clk/pxa/Makefile b/drivers/clk/pxa/Makefile
new file mode 100644
index 000000000000..4ff2abcd500b
--- /dev/null
+++ b/drivers/clk/pxa/Makefile
@@ -0,0 +1,2 @@
+obj-y += clk-pxa.o
+obj-$(CONFIG_PXA27x) += clk-pxa27x.o
diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c
new file mode 100644
index 000000000000..ef3c05389c0a
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa.c
@@ -0,0 +1,97 @@
+/*
+ * Marvell PXA family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Common clock code for PXA clocks ("CKEN" type clocks + DT)
+ *
+ * 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; version 2 of the License.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pxa-clock.h>
+#include "clk-pxa.h"
+
+DEFINE_SPINLOCK(lock);
+
+static struct clk *pxa_clocks[CLK_MAX];
+static struct clk_onecell_data onecell_data = {
+ .clks = pxa_clocks,
+ .clk_num = CLK_MAX,
+};
+
+#define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk_cken, hw)
+
+static unsigned long cken_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct pxa_clk_cken *pclk = to_pxa_clk(hw);
+ struct clk_fixed_factor *fix;
+
+ if (!pclk->is_in_low_power || pclk->is_in_low_power())
+ fix = &pclk->lp;
+ else
+ fix = &pclk->hp;
+ fix->hw.clk = hw->clk;
+ return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate);
+}
+
+static struct clk_ops cken_rate_ops = {
+ .recalc_rate = cken_recalc_rate,
+};
+
+static u8 cken_get_parent(struct clk_hw *hw)
+{
+ struct pxa_clk_cken *pclk = to_pxa_clk(hw);
+
+ if (!pclk->is_in_low_power)
+ return 0;
+ return pclk->is_in_low_power() ? 0 : 1;
+}
+
+static struct clk_ops cken_mux_ops = {
+ .get_parent = cken_get_parent,
+ .set_parent = dummy_clk_set_parent,
+};
+
+void __init clkdev_pxa_register(int ckid, const char *con_id,
+ const char *dev_id, struct clk *clk)
+{
+ if (!IS_ERR(clk) && (ckid != CLK_NONE))
+ pxa_clocks[ckid] = clk;
+ if (!IS_ERR(clk))
+ clk_register_clkdev(clk, con_id, dev_id);
+}
+
+int __init clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks)
+{
+ int i;
+ struct pxa_clk_cken *pclk;
+ struct clk *clk;
+
+ for (i = 0; i < nb_clks; i++) {
+ pclk = clks + i;
+ pclk->gate.lock = &lock;
+ clk = clk_register_composite(NULL, pclk->name,
+ pclk->parent_names, 2,
+ &pclk->hw, &cken_mux_ops,
+ &pclk->hw, &cken_rate_ops,
+ &pclk->gate.hw, &clk_gate_ops,
+ pclk->flags);
+ clkdev_pxa_register(pclk->ckid, pclk->con_id, pclk->dev_id,
+ clk);
+ }
+ return 0;
+}
+
+static void __init pxa_dt_clocks_init(struct device_node *np)
+{
+ of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data);
+}
+CLK_OF_DECLARE(pxa_clks, "marvell,pxa-clocks", pxa_dt_clocks_init);
diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h
new file mode 100644
index 000000000000..5fe219d06b49
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa.h
@@ -0,0 +1,107 @@
+/*
+ * Marvell PXA family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Common clock code for PXA clocks ("CKEN" type clocks + DT)
+ *
+ * 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; version 2 of the License.
+ *
+ */
+#ifndef _CLK_PXA_
+#define _CLK_PXA_
+
+#define PARENTS(name) \
+ static const char *name ## _parents[] __initconst
+#define MUX_RO_RATE_RO_OPS(name, clk_name) \
+ static struct clk_hw name ## _mux_hw; \
+ static struct clk_hw name ## _rate_hw; \
+ static struct clk_ops name ## _mux_ops = { \
+ .get_parent = name ## _get_parent, \
+ .set_parent = dummy_clk_set_parent, \
+ }; \
+ static struct clk_ops name ## _rate_ops = { \
+ .recalc_rate = name ## _get_rate, \
+ }; \
+ static struct clk *clk_register_ ## name(void) \
+ { \
+ return clk_register_composite(NULL, clk_name, \
+ name ## _parents, \
+ ARRAY_SIZE(name ## _parents), \
+ &name ## _mux_hw, &name ## _mux_ops, \
+ &name ## _rate_hw, &name ## _rate_ops, \
+ NULL, NULL, CLK_GET_RATE_NOCACHE); \
+ }
+
+#define RATE_RO_OPS(name, clk_name) \
+ static struct clk_hw name ## _rate_hw; \
+ static struct clk_ops name ## _rate_ops = { \
+ .recalc_rate = name ## _get_rate, \
+ }; \
+ static struct clk *clk_register_ ## name(void) \
+ { \
+ return clk_register_composite(NULL, clk_name, \
+ name ## _parents, \
+ ARRAY_SIZE(name ## _parents), \
+ NULL, NULL, \
+ &name ## _rate_hw, &name ## _rate_ops, \
+ NULL, NULL, CLK_GET_RATE_NOCACHE); \
+ }
+
+/*
+ * CKEN clock type
+ * This clock takes it source from 2 possible parents :
+ * - a low power parent
+ * - a normal parent
+ *
+ * +------------+ +-----------+
+ * | Low Power | --- | x mult_lp |
+ * | Clock | | / div_lp |\
+ * +------------+ +-----------+ \+-----+ +-----------+
+ * | Mux |---| CKEN gate |
+ * +------------+ +-----------+ /+-----+ +-----------+
+ * | High Power | | x mult_hp |/
+ * | Clock | --- | / div_hp |
+ * +------------+ +-----------+
+ */
+struct pxa_clk_cken {
+ struct clk_hw hw;
+ int ckid;
+ const char *name;
+ const char *dev_id;
+ const char *con_id;
+ const char **parent_names;
+ struct clk_fixed_factor lp;
+ struct clk_fixed_factor hp;
+ struct clk_gate gate;
+ bool (*is_in_low_power)(void);
+ const unsigned long flags;
+};
+
+#define PXA_CKEN(_dev_id, _con_id, _name, parents, _mult_lp, _div_lp, \
+ _mult_hp, _div_hp, is_lp, _cken_reg, _cken_bit, flag) \
+ { .ckid = CLK_ ## _name, .name = #_name, \
+ .dev_id = _dev_id, .con_id = _con_id, .parent_names = parents,\
+ .lp = { .mult = _mult_lp, .div = _div_lp }, \
+ .hp = { .mult = _mult_hp, .div = _div_hp }, \
+ .is_in_low_power = is_lp, \
+ .gate = { .reg = (void __iomem *)_cken_reg, .bit_idx = _cken_bit }, \
+ .flags = flag, \
+ }
+#define PXA_CKEN_1RATE(dev_id, con_id, name, parents, cken_reg, \
+ cken_bit, flag) \
+ PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1, \
+ NULL, cken_reg, cken_bit, flag)
+
+static int dummy_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ return 0;
+}
+
+extern void clkdev_pxa_register(int ckid, const char *con_id,
+ const char *dev_id, struct clk *clk);
+extern int clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks);
+
+#endif
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
new file mode 100644
index 000000000000..b345cc791e5d
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa27x.c
@@ -0,0 +1,370 @@
+/*
+ * Marvell PXA27x family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Heavily inspired from former arch/arm/mach-pxa/clock.c.
+ *
+ * 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; version 2 of the License.
+ *
+ */
+#include <linux/clk-provider.h>
+#include <mach/pxa2xx-regs.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pxa-clock.h>
+#include "clk-pxa.h"
+
+#define KHz 1000
+#define MHz (1000 * 1000)
+
+enum {
+ PXA_CORE_13Mhz = 0,
+ PXA_CORE_RUN,
+ PXA_CORE_TURBO,
+};
+
+enum {
+ PXA_BUS_13Mhz = 0,
+ PXA_BUS_RUN,
+};
+
+enum {
+ PXA_LCD_13Mhz = 0,
+ PXA_LCD_RUN,
+};
+
+enum {
+ PXA_MEM_13Mhz = 0,
+ PXA_MEM_SYSTEM_BUS,
+ PXA_MEM_RUN,
+};
+
+static const char * const get_freq_khz[] = {
+ "core", "run", "cpll", "memory",
+ "system_bus"
+};
+
+/*
+ * Get the clock frequency as reflected by CCSR and the turbo flag.
+ * We assume these values have been applied via a fcs.
+ * If info is not 0 we also display the current settings.
+ */
+unsigned int pxa27x_get_clk_frequency_khz(int info)
+{
+ struct clk *clk;
+ unsigned long clks[5];
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ clk = clk_get(NULL, get_freq_khz[i]);
+ if (IS_ERR(clk)) {
+ clks[i] = 0;
+ } else {
+ clks[i] = clk_get_rate(clk);
+ clk_put(clk);
+ }
+ }
+ if (info) {
+ pr_info("Run Mode clock: %ld.%02ldMHz\n",
+ clks[1] / 1000000, (clks[1] % 1000000) / 10000);
+ pr_info("Turbo Mode clock: %ld.%02ldMHz\n",
+ clks[2] / 1000000, (clks[2] % 1000000) / 10000);
+ pr_info("Memory clock: %ld.%02ldMHz\n",
+ clks[3] / 1000000, (clks[3] % 1000000) / 10000);
+ pr_info("System bus clock: %ld.%02ldMHz\n",
+ clks[4] / 1000000, (clks[4] % 1000000) / 10000);
+ }
+ return (unsigned int)clks[0];
+}
+
+bool pxa27x_is_ppll_disabled(void)
+{
+ unsigned long ccsr = CCSR;
+
+ return ccsr & (1 << CCCR_PPDIS_BIT);
+}
+
+#define PXA27X_CKEN(dev_id, con_id, parents, mult_hp, div_hp, \
+ bit, is_lp, flags) \
+ PXA_CKEN(dev_id, con_id, bit, parents, 1, 1, mult_hp, div_hp, \
+ is_lp, &CKEN, CKEN_ ## bit, flags)
+#define PXA27X_PBUS_CKEN(dev_id, con_id, bit, mult_hp, div_hp, delay) \
+ PXA27X_CKEN(dev_id, con_id, pxa27x_pbus_parents, mult_hp, \
+ div_hp, bit, pxa27x_is_ppll_disabled, 0)
+
+PARENTS(pxa27x_pbus) = { "osc_13mhz", "ppll_312mhz" };
+PARENTS(pxa27x_sbus) = { "system_bus", "system_bus" };
+PARENTS(pxa27x_32Mhz_bus) = { "osc_32_768khz", "osc_32_768khz" };
+PARENTS(pxa27x_lcd_bus) = { "lcd_base", "lcd_base" };
+PARENTS(pxa27x_membus) = { "lcd_base", "lcd_base" };
+
+#define PXA27X_CKEN_1RATE(dev_id, con_id, bit, parents, delay) \
+ PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \
+ &CKEN, CKEN_ ## bit, 0)
+#define PXA27X_CKEN_1RATE_AO(dev_id, con_id, bit, parents, delay) \
+ PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \
+ &CKEN, CKEN_ ## bit, CLK_IGNORE_UNUSED)
+
+static struct pxa_clk_cken pxa27x_clocks[] = {
+ PXA27X_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 2, 42, 1),
+ PXA27X_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 2, 42, 1),
+ PXA27X_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 2, 42, 1),
+ PXA27X_PBUS_CKEN("pxa2xx-i2s", NULL, I2S, 2, 51, 0),
+ PXA27X_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 19, 0),
+ PXA27X_PBUS_CKEN("pxa27x-udc", NULL, USB, 2, 13, 5),
+ PXA27X_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC, 2, 32, 0),
+ PXA27X_PBUS_CKEN("pxa2xx-ir", "FICPCLK", FICP, 2, 13, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ohci", NULL, USBHOST, 2, 13, 0),
+ PXA27X_PBUS_CKEN("pxa2xx-i2c.1", NULL, PWRI2C, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ssp.0", NULL, SSP1, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ssp.1", NULL, SSP2, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ssp.2", NULL, SSP3, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 24, 0),
+ PXA27X_PBUS_CKEN(NULL, "MSLCLK", MSL, 2, 13, 0),
+ PXA27X_PBUS_CKEN(NULL, "USIMCLK", USIM, 2, 13, 0),
+ PXA27X_PBUS_CKEN(NULL, "MSTKCLK", MEMSTK, 2, 32, 0),
+ PXA27X_PBUS_CKEN(NULL, "AC97CLK", AC97, 1, 1, 0),
+ PXA27X_PBUS_CKEN(NULL, "AC97CONFCLK", AC97CONF, 1, 1, 0),
+ PXA27X_PBUS_CKEN(NULL, "OSTIMER0", OSTIMER, 1, 96, 0),
+
+ PXA27X_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD,
+ pxa27x_32Mhz_bus_parents, 0),
+ PXA27X_CKEN_1RATE(NULL, "IMCLK", IM, pxa27x_sbus_parents, 0),
+ PXA27X_CKEN_1RATE("pxa2xx-fb", NULL, LCD, pxa27x_lcd_bus_parents, 0),
+ PXA27X_CKEN_1RATE("pxa27x-camera.0", NULL, CAMERA,
+ pxa27x_lcd_bus_parents, 0),
+ PXA27X_CKEN_1RATE_AO("pxa2xx-pcmcia", NULL, MEMC,
+ pxa27x_membus_parents, 0),
+
+};
+
+static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int t, ht;
+ unsigned int l, L, n2, N;
+ unsigned long ccsr = CCSR;
+
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ t = clkcfg & (1 << 0);
+ ht = clkcfg & (1 << 2);
+
+ l = ccsr & CCSR_L_MASK;
+ n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT;
+ L = l * parent_rate;
+ N = (L * n2) / 2;
+
+ return t ? N : L;
+}
+PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" };
+RATE_RO_OPS(clk_pxa27x_cpll, "cpll");
+
+static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned int l, osc_forced;
+ unsigned long ccsr = CCSR;
+ unsigned long cccr = CCCR;
+
+ l = ccsr & CCSR_L_MASK;
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced) {
+ if (cccr & (1 << CCCR_LCD_26_BIT))
+ return parent_rate * 2;
+ else
+ return parent_rate;
+ }
+
+ if (l <= 7)
+ return parent_rate;
+ if (l <= 16)
+ return parent_rate / 2;
+ return parent_rate / 4;
+}
+
+static u8 clk_pxa27x_lcd_base_get_parent(struct clk_hw *hw)
+{
+ unsigned int osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced)
+ return PXA_LCD_13Mhz;
+ else
+ return PXA_LCD_RUN;
+}
+
+PARENTS(clk_pxa27x_lcd_base) = { "osc_13mhz", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_lcd_base, "lcd_base");
+
+static void __init pxa27x_register_plls(void)
+{
+ clk_register_fixed_rate(NULL, "osc_13mhz", NULL,
+ CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
+ 13 * MHz);
+ clk_register_fixed_rate(NULL, "osc_32_768khz", NULL,
+ CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
+ 32768 * KHz);
+ clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0);
+ clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1);
+}
+
+static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int t, ht, b, osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ t = clkcfg & (1 << 0);
+ ht = clkcfg & (1 << 2);
+ b = clkcfg & (1 << 3);
+
+ if (osc_forced)
+ return parent_rate;
+ if (ht)
+ return parent_rate / 2;
+ else
+ return parent_rate;
+}
+
+static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw)
+{
+ unsigned long clkcfg;
+ unsigned int t, ht, b, osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced)
+ return PXA_CORE_13Mhz;
+
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ t = clkcfg & (1 << 0);
+ ht = clkcfg & (1 << 2);
+ b = clkcfg & (1 << 3);
+
+ if (ht || t)
+ return PXA_CORE_TURBO;
+ return PXA_CORE_RUN;
+}
+PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core");
+
+static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long ccsr = CCSR;
+ unsigned int n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT;
+
+ return (parent_rate / n2) * 2;
+}
+PARENTS(clk_pxa27x_run) = { "cpll" };
+RATE_RO_OPS(clk_pxa27x_run, "run");
+
+static void __init pxa27x_register_core(void)
+{
+ clk_register_clk_pxa27x_cpll();
+ clk_register_clk_pxa27x_run();
+
+ clkdev_pxa_register(CLK_CORE, "core", NULL,
+ clk_register_clk_pxa27x_core());
+}
+
+static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int b, osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ b = clkcfg & (1 << 3);
+
+ if (osc_forced)
+ return parent_rate;
+ if (b)
+ return parent_rate / 2;
+ else
+ return parent_rate;
+}
+
+static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw)
+{
+ unsigned int osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced)
+ return PXA_BUS_13Mhz;
+ else
+ return PXA_BUS_RUN;
+}
+
+PARENTS(clk_pxa27x_system_bus) = { "osc_13mhz", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_system_bus, "system_bus");
+
+static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned int a, l, osc_forced;
+ unsigned long cccr = CCCR;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ a = cccr & CCCR_A_BIT;
+ l = ccsr & CCSR_L_MASK;
+
+ if (osc_forced || a)
+ return parent_rate;
+ if (l <= 10)
+ return parent_rate;
+ if (l <= 20)
+ return parent_rate / 2;
+ return parent_rate / 4;
+}
+
+static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw)
+{
+ unsigned int osc_forced, a;
+ unsigned long cccr = CCCR;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ a = cccr & CCCR_A_BIT;
+ if (osc_forced)
+ return PXA_MEM_13Mhz;
+ if (a)
+ return PXA_MEM_SYSTEM_BUS;
+ else
+ return PXA_MEM_RUN;
+}
+
+PARENTS(clk_pxa27x_memory) = { "osc_13mhz", "system_bus", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_memory, "memory");
+
+static void __init pxa27x_base_clocks_init(void)
+{
+ pxa27x_register_plls();
+ pxa27x_register_core();
+ clk_register_clk_pxa27x_system_bus();
+ clk_register_clk_pxa27x_memory();
+ clk_register_clk_pxa27x_lcd_base();
+}
+
+static int __init pxa27x_clocks_init(void)
+{
+ pxa27x_base_clocks_init();
+ return clk_pxa_cken_init(pxa27x_clocks, ARRAY_SIZE(pxa27x_clocks));
+}
+postcore_initcall(pxa27x_clocks_init);
diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c
index 9db03d3b1657..b823bc3b6250 100644
--- a/drivers/clk/qcom/clk-pll.c
+++ b/drivers/clk/qcom/clk-pll.c
@@ -97,7 +97,7 @@ static unsigned long
clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct clk_pll *pll = to_clk_pll(hw);
- u32 l, m, n;
+ u32 l, m, n, config;
unsigned long rate;
u64 tmp;
@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
do_div(tmp, n);
rate += tmp;
}
+ if (pll->post_div_width) {
+ regmap_read(pll->clkr.regmap, pll->config_reg, &config);
+ config >>= pll->post_div_shift;
+ config &= BIT(pll->post_div_width) - 1;
+ rate /= config + 1;
+ }
+
return rate;
}
+static const
+struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
+{
+ if (!f)
+ return NULL;
+
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ return NULL;
+}
+
+static long
+clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ const struct pll_freq_tbl *f;
+
+ f = find_freq(pll->freq_tbl, rate);
+ if (!f)
+ return clk_pll_recalc_rate(hw, *p_rate);
+
+ return f->freq;
+}
+
+static int
+clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ const struct pll_freq_tbl *f;
+ bool enabled;
+ u32 mode;
+ u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
+
+ f = find_freq(pll->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
+ enabled = (mode & enable_mask) == enable_mask;
+
+ if (enabled)
+ clk_pll_disable(hw);
+
+ regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
+ regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
+ regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
+ regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
+
+ if (enabled)
+ clk_pll_enable(hw);
+
+ return 0;
+}
+
const struct clk_ops clk_pll_ops = {
.enable = clk_pll_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_determine_rate,
+ .set_rate = clk_pll_set_rate,
};
EXPORT_SYMBOL_GPL(clk_pll_ops);
diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h
index 3003e9962472..c9c0cda306d0 100644
--- a/drivers/clk/qcom/clk-pll.h
+++ b/drivers/clk/qcom/clk-pll.h
@@ -18,6 +18,21 @@
#include "clk-regmap.h"
/**
+ * struct pll_freq_tbl - PLL frequency table
+ * @l: L value
+ * @m: M value
+ * @n: N value
+ * @ibits: internal values
+ */
+struct pll_freq_tbl {
+ unsigned long freq;
+ u16 l;
+ u16 m;
+ u16 n;
+ u32 ibits;
+};
+
+/**
* struct clk_pll - phase locked loop (PLL)
* @l_reg: L register
* @m_reg: M register
@@ -26,6 +41,7 @@
* @mode_reg: mode register
* @status_reg: status register
* @status_bit: ANDed with @status_reg to determine if PLL is enabled
+ * @freq_tbl: PLL frequency table
* @hw: handle between common and hardware-specific interfaces
*/
struct clk_pll {
@@ -36,6 +52,10 @@ struct clk_pll {
u32 mode_reg;
u32 status_reg;
u8 status_bit;
+ u8 post_div_width;
+ u8 post_div_shift;
+
+ const struct pll_freq_tbl *freq_tbl;
struct clk_regmap clkr;
};
diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c
index b638c5846dbf..b6e6959e89aa 100644
--- a/drivers/clk/qcom/clk-rcg.c
+++ b/drivers/clk/qcom/clk-rcg.c
@@ -21,6 +21,7 @@
#include <asm/div64.h>
#include "clk-rcg.h"
+#include "common.h"
static u32 ns_to_src(struct src_sel *s, u32 ns)
{
@@ -67,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
int num_parents = __clk_get_num_parents(hw->clk);
- u32 ns, ctl;
+ u32 ns, reg;
int bank;
int i;
struct src_sel *s;
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
- bank = reg_to_bank(rcg, ctl);
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ bank = reg_to_bank(rcg, reg);
s = &rcg->s[bank];
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
+ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
ns = ns_to_src(s, ns);
for (i = 0; i < num_parents; i++)
@@ -192,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)
static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
{
- u32 ns, md, ctl, *regp;
+ u32 ns, md, reg;
int bank, new_bank;
struct mn *mn;
struct pre_div *p;
struct src_sel *s;
bool enabled;
- u32 md_reg;
- u32 bank_reg;
+ u32 md_reg, ns_reg;
bool banked_mn = !!rcg->mn[1].width;
+ bool banked_p = !!rcg->p[1].pre_div_width;
struct clk_hw *hw = &rcg->clkr.hw;
enabled = __clk_is_enabled(hw->clk);
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
-
- if (banked_mn) {
- regp = &ctl;
- bank_reg = rcg->clkr.enable_reg;
- } else {
- regp = &ns;
- bank_reg = rcg->ns_reg;
- }
-
- bank = reg_to_bank(rcg, *regp);
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ bank = reg_to_bank(rcg, reg);
new_bank = enabled ? !bank : bank;
+ ns_reg = rcg->ns_reg[new_bank];
+ regmap_read(rcg->clkr.regmap, ns_reg, &ns);
+
if (banked_mn) {
mn = &rcg->mn[new_bank];
md_reg = rcg->md_reg[new_bank];
ns |= BIT(mn->mnctr_reset_bit);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
regmap_read(rcg->clkr.regmap, md_reg, &md);
md = mn_to_md(mn, f->m, f->n, md);
regmap_write(rcg->clkr.regmap, md_reg, md);
ns = mn_to_ns(mn, f->m, f->n, ns);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
- ctl = mn_to_reg(mn, f->m, f->n, ctl);
- regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
+ /* Two NS registers means mode control is in NS register */
+ if (rcg->ns_reg[0] != rcg->ns_reg[1]) {
+ ns = mn_to_reg(mn, f->m, f->n, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+ } else {
+ reg = mn_to_reg(mn, f->m, f->n, reg);
+ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
+ }
ns &= ~BIT(mn->mnctr_reset_bit);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
- } else {
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+ }
+
+ if (banked_p) {
p = &rcg->p[new_bank];
ns = pre_div_to_ns(p, f->pre_div - 1, ns);
}
s = &rcg->s[new_bank];
ns = src_to_ns(s, s->parent_map[f->src], ns);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
if (enabled) {
- *regp ^= BIT(rcg->mux_sel_bit);
- regmap_write(rcg->clkr.regmap, bank_reg, *regp);
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ reg ^= BIT(rcg->mux_sel_bit);
+ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
}
}
static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
- u32 ns, ctl, md, reg;
+ u32 ns, md, reg;
int bank;
struct freq_tbl f = { 0 };
bool banked_mn = !!rcg->mn[1].width;
+ bool banked_p = !!rcg->p[1].pre_div_width;
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
- reg = banked_mn ? ctl : ns;
-
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);
+ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+
if (banked_mn) {
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
f.m = md_to_m(&rcg->mn[bank], md);
f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
- } else {
- f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
}
- f.src = index;
+ if (banked_p)
+ f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
+
+ f.src = index;
configure_bank(rcg, &f);
return 0;
@@ -336,41 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
u32 m, n, pre_div, ns, md, mode, reg;
int bank;
struct mn *mn;
+ bool banked_p = !!rcg->p[1].pre_div_width;
bool banked_mn = !!rcg->mn[1].width;
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
-
- if (banked_mn)
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &reg);
- else
- reg = ns;
-
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);
+ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+ m = n = pre_div = mode = 0;
+
if (banked_mn) {
mn = &rcg->mn[bank];
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
m = md_to_m(mn, md);
n = ns_m_to_n(mn, ns, m);
+ /* Two NS registers means mode control is in NS register */
+ if (rcg->ns_reg[0] != rcg->ns_reg[1])
+ reg = ns;
mode = reg_to_mnctr_mode(mn, reg);
- return calc_rate(parent_rate, m, n, mode, 0);
- } else {
- pre_div = ns_to_pre_div(&rcg->p[bank], ns);
- return calc_rate(parent_rate, 0, 0, 0, pre_div);
}
-}
-static const
-struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
-{
- if (!f)
- return NULL;
-
- for (; f->freq; f++)
- if (rate <= f->freq)
- return f;
+ if (banked_p)
+ pre_div = ns_to_pre_div(&rcg->p[bank], ns);
- return NULL;
+ return calc_rate(parent_rate, m, n, mode, pre_div);
}
static long _freq_tbl_determine_rate(struct clk_hw *hw,
@@ -379,7 +372,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw,
{
unsigned long clk_flags;
- f = find_freq(f, rate);
+ f = qcom_find_freq(f, rate);
if (!f)
return -EINVAL;
@@ -477,7 +470,7 @@ static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_rcg *rcg = to_clk_rcg(hw);
const struct freq_tbl *f;
- f = find_freq(rcg->freq_tbl, rate);
+ f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
@@ -497,7 +490,7 @@ static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
const struct freq_tbl *f;
- f = find_freq(rcg->freq_tbl, rate);
+ f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index ba0523cefd2e..687e41f91d7c 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -103,8 +103,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
* struct clk_dyn_rcg - root clock generator with glitch free mux
*
* @mux_sel_bit: bit to switch glitch free mux
- * @ns_reg: NS register
+ * @ns_reg: NS0 and NS1 register
* @md_reg: MD0 and MD1 register
+ * @bank_reg: register to XOR @mux_sel_bit into to switch glitch free mux
* @mn: mn counter (banked)
* @s: source selector (banked)
* @freq_tbl: frequency table
@@ -113,8 +114,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
*
*/
struct clk_dyn_rcg {
- u32 ns_reg;
+ u32 ns_reg[2];
u32 md_reg[2];
+ u32 bank_reg;
u8 mux_sel_bit;
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index cd185d5cc67a..cfa9eb4fe9ca 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -24,6 +24,7 @@
#include <asm/div64.h>
#include "clk-rcg.h"
+#include "common.h"
#define CMD_REG 0x0
#define CMD_UPDATE BIT(0)
@@ -172,27 +173,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return calc_rate(parent_rate, m, n, mode, hid_div);
}
-static const
-struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
-{
- if (!f)
- return NULL;
-
- for (; f->freq; f++)
- if (rate <= f->freq)
- return f;
-
- /* Default to our fastest rate */
- return f - 1;
-}
-
static long _freq_tbl_determine_rate(struct clk_hw *hw,
const struct freq_tbl *f, unsigned long rate,
unsigned long *p_rate, struct clk **p)
{
unsigned long clk_flags;
- f = find_freq(f, rate);
+ f = qcom_find_freq(f, rate);
if (!f)
return -EINVAL;
@@ -268,7 +255,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const struct freq_tbl *f;
- f = find_freq(rcg->freq_tbl, rate);
+ f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index eeb3eea01f4c..e20d947db3e5 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -18,6 +18,7 @@
#include <linux/reset-controller.h>
#include "common.h"
+#include "clk-rcg.h"
#include "clk-regmap.h"
#include "reset.h"
@@ -27,6 +28,21 @@ struct qcom_cc {
struct clk *clks[];
};
+const
+struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
+{
+ if (!f)
+ return NULL;
+
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ /* Default to our fastest rate */
+ return f - 1;
+}
+EXPORT_SYMBOL_GPL(qcom_find_freq);
+
struct regmap *
qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
{
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 2765e9d3da97..f519322acdf3 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -18,6 +18,7 @@ struct regmap_config;
struct clk_regmap;
struct qcom_reset_map;
struct regmap;
+struct freq_tbl;
struct qcom_cc_desc {
const struct regmap_config *config;
@@ -27,6 +28,9 @@ struct qcom_cc_desc {
size_t num_resets;
};
+extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
+ unsigned long rate);
+
extern struct regmap *qcom_cc_map(struct platform_device *pdev,
const struct qcom_cc_desc *desc);
extern int qcom_cc_really_probe(struct platform_device *pdev,
diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
index 3b83b7dd78c7..5cd62a709ac7 100644
--- a/drivers/clk/qcom/gcc-ipq806x.c
+++ b/drivers/clk/qcom/gcc-ipq806x.c
@@ -32,6 +32,33 @@
#include "clk-branch.h"
#include "reset.h"
+static struct clk_pll pll0 = {
+ .l_reg = 0x30c4,
+ .m_reg = 0x30c8,
+ .n_reg = 0x30cc,
+ .config_reg = 0x30d4,
+ .mode_reg = 0x30c0,
+ .status_reg = 0x30d8,
+ .status_bit = 16,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pll0",
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap pll0_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll0_vote",
+ .parent_names = (const char *[]){ "pll0" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
static struct clk_pll pll3 = {
.l_reg = 0x3164,
.m_reg = 0x3168,
@@ -154,7 +181,7 @@ static const u8 gcc_pxo_pll8_pll0[] = {
static const char *gcc_pxo_pll8_pll0_map[] = {
"pxo",
"pll8_vote",
- "pll0",
+ "pll0_vote",
};
static struct freq_tbl clk_tbl_gsbi_uart[] = {
@@ -2133,6 +2160,8 @@ static struct clk_branch usb_fs1_h_clk = {
};
static struct clk_regmap *gcc_ipq806x_clks[] = {
+ [PLL0] = &pll0.clkr,
+ [PLL0_VOTE] = &pll0_vote,
[PLL3] = &pll3.clkr,
[PLL8] = &pll8.clkr,
[PLL8_VOTE] = &pll8_vote,
diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c
index 751eea376a2b..dab988ab8cf1 100644
--- a/drivers/clk/qcom/mmcc-apq8084.c
+++ b/drivers/clk/qcom/mmcc-apq8084.c
@@ -3341,7 +3341,6 @@ static struct platform_driver mmcc_apq8084_driver = {
.remove = mmcc_apq8084_remove,
.driver = {
.name = "mmcc-apq8084",
- .owner = THIS_MODULE,
.of_match_table = mmcc_apq8084_match_table,
},
};
diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c
index 2e80a219b8ea..e8b33bbc362f 100644
--- a/drivers/clk/qcom/mmcc-msm8960.c
+++ b/drivers/clk/qcom/mmcc-msm8960.c
@@ -773,9 +773,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = {
};
static struct clk_dyn_rcg gfx2d0_src = {
- .ns_reg = 0x0070,
+ .ns_reg[0] = 0x0070,
+ .ns_reg[1] = 0x0070,
.md_reg[0] = 0x0064,
.md_reg[1] = 0x0068,
+ .bank_reg = 0x0060,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 25,
@@ -831,9 +833,11 @@ static struct clk_branch gfx2d0_clk = {
};
static struct clk_dyn_rcg gfx2d1_src = {
- .ns_reg = 0x007c,
+ .ns_reg[0] = 0x007c,
+ .ns_reg[1] = 0x007c,
.md_reg[0] = 0x0078,
.md_reg[1] = 0x006c,
+ .bank_reg = 0x0074,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 25,
@@ -930,9 +934,11 @@ static struct freq_tbl clk_tbl_gfx3d_8064[] = {
};
static struct clk_dyn_rcg gfx3d_src = {
- .ns_reg = 0x008c,
+ .ns_reg[0] = 0x008c,
+ .ns_reg[1] = 0x008c,
.md_reg[0] = 0x0084,
.md_reg[1] = 0x0088,
+ .bank_reg = 0x0080,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 25,
@@ -1006,9 +1012,11 @@ static struct freq_tbl clk_tbl_vcap[] = {
};
static struct clk_dyn_rcg vcap_src = {
- .ns_reg = 0x021c,
+ .ns_reg[0] = 0x021c,
+ .ns_reg[1] = 0x021c,
.md_reg[0] = 0x01ec,
.md_reg[1] = 0x0218,
+ .bank_reg = 0x0178,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 23,
@@ -1211,9 +1219,11 @@ static struct freq_tbl clk_tbl_mdp[] = {
};
static struct clk_dyn_rcg mdp_src = {
- .ns_reg = 0x00d0,
+ .ns_reg[0] = 0x00d0,
+ .ns_reg[1] = 0x00d0,
.md_reg[0] = 0x00c4,
.md_reg[1] = 0x00c8,
+ .bank_reg = 0x00c0,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 31,
@@ -1318,7 +1328,9 @@ static struct freq_tbl clk_tbl_rot[] = {
};
static struct clk_dyn_rcg rot_src = {
- .ns_reg = 0x00e8,
+ .ns_reg[0] = 0x00e8,
+ .ns_reg[1] = 0x00e8,
+ .bank_reg = 0x00e8,
.p[0] = {
.pre_div_shift = 22,
.pre_div_width = 4,
@@ -1542,9 +1554,11 @@ static struct freq_tbl clk_tbl_vcodec[] = {
};
static struct clk_dyn_rcg vcodec_src = {
- .ns_reg = 0x0100,
+ .ns_reg[0] = 0x0100,
+ .ns_reg[1] = 0x0100,
.md_reg[0] = 0x00fc,
.md_reg[1] = 0x0128,
+ .bank_reg = 0x00f8,
.mn[0] = {
.mnctr_en_bit = 5,
.mnctr_reset_bit = 31,
@@ -2679,7 +2693,6 @@ static struct platform_driver mmcc_msm8960_driver = {
.remove = mmcc_msm8960_remove,
.driver = {
.name = "mmcc-msm8960",
- .owner = THIS_MODULE,
.of_match_table = mmcc_msm8960_match_table,
},
};
diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c
index bc8f519c47aa..be94c54a9a4f 100644
--- a/drivers/clk/qcom/mmcc-msm8974.c
+++ b/drivers/clk/qcom/mmcc-msm8974.c
@@ -2570,7 +2570,6 @@ static struct platform_driver mmcc_msm8974_driver = {
.remove = mmcc_msm8974_remove,
.driver = {
.name = "mmcc-msm8974",
- .owner = THIS_MODULE,
.of_match_table = mmcc_msm8974_match_table,
},
};
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index ee6b077381e1..bd8514d63634 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -5,6 +5,7 @@
obj-y += clk-rockchip.o
obj-y += clk.o
obj-y += clk-pll.o
+obj-y += clk-cpu.o
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
obj-y += clk-rk3188.o
diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c
new file mode 100644
index 000000000000..75c8c45ef728
--- /dev/null
+++ b/drivers/clk/rockchip/clk-cpu.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on clk/samsung/clk-cpu.c
+ * Copyright (c) 2014 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.
+ *
+ * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs.
+ * The CPU clock is typically derived from a hierarchy of clock
+ * blocks which includes mux and divider blocks. There are a number of other
+ * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
+ * clock for CPU domain. The rates of these auxiliary clocks are related to the
+ * CPU clock rate and this relation is usually specified in the hardware manual
+ * of the SoC or supplied after the SoC characterization.
+ *
+ * The below implementation of the CPU clock allows the rate changes of the CPU
+ * clock and the corresponding rate changes of the auxillary clocks of the CPU
+ * domain. The platform clock driver provides a clock register configuration
+ * for each configurable rate which is then used to program the clock hardware
+ * registers to acheive a fast co-oridinated rate change for all the CPU domain
+ * clocks.
+ *
+ * On a rate change request for the CPU clock, the rate change is propagated
+ * upto the PLL supplying the clock to the CPU domain clock blocks. While the
+ * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
+ * alternate clock source. If required, the alternate clock source is divided
+ * down in order to keep the output clock rate within the previous OPP limits.
+ */
+
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+/**
+ * struct rockchip_cpuclk: information about clock supplied to a CPU core.
+ * @hw: handle between ccf and cpu clock.
+ * @alt_parent: alternate parent clock to use when switching the speed
+ * of the primary parent clock.
+ * @reg_base: base register for cpu-clock values.
+ * @clk_nb: clock notifier registered for changes in clock speed of the
+ * primary parent clock.
+ * @rate_count: number of rates in the rate_table
+ * @rate_table: pll-rates and their associated dividers
+ * @reg_data: cpu-specific register settings
+ * @lock: clock lock
+ */
+struct rockchip_cpuclk {
+ struct clk_hw hw;
+
+ struct clk_mux cpu_mux;
+ const struct clk_ops *cpu_mux_ops;
+
+ struct clk *alt_parent;
+ void __iomem *reg_base;
+ struct notifier_block clk_nb;
+ unsigned int rate_count;
+ struct rockchip_cpuclk_rate_table *rate_table;
+ const struct rockchip_cpuclk_reg_data *reg_data;
+ spinlock_t *lock;
+};
+
+#define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw)
+#define to_rockchip_cpuclk_nb(nb) \
+ container_of(nb, struct rockchip_cpuclk, clk_nb)
+
+static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings(
+ struct rockchip_cpuclk *cpuclk, unsigned long rate)
+{
+ const struct rockchip_cpuclk_rate_table *rate_table =
+ cpuclk->rate_table;
+ int i;
+
+ for (i = 0; i < cpuclk->rate_count; i++) {
+ if (rate == rate_table[i].prate)
+ return &rate_table[i];
+ }
+
+ return NULL;
+}
+
+static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw);
+ const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
+ u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg);
+
+ clksel0 >>= reg_data->div_core_shift;
+ clksel0 &= reg_data->div_core_mask;
+ return parent_rate / (clksel0 + 1);
+}
+
+static const struct clk_ops rockchip_cpuclk_ops = {
+ .recalc_rate = rockchip_cpuclk_recalc_rate,
+};
+
+static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
+ const struct rockchip_cpuclk_rate_table *rate)
+{
+ int i;
+
+ /* alternate parent is active now. set the dividers */
+ for (i = 0; i < ARRAY_SIZE(rate->divs); i++) {
+ const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i];
+
+ if (!clksel->reg)
+ continue;
+
+ pr_debug("%s: setting reg 0x%x to 0x%x\n",
+ __func__, clksel->reg, clksel->val);
+ writel(clksel->val , cpuclk->reg_base + clksel->reg);
+ }
+}
+
+static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
+ struct clk_notifier_data *ndata)
+{
+ const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
+ unsigned long alt_prate, alt_div;
+
+ alt_prate = clk_get_rate(cpuclk->alt_parent);
+
+ spin_lock(cpuclk->lock);
+
+ /*
+ * If the old parent clock speed is less than the clock speed
+ * of the alternate parent, then it should be ensured that at no point
+ * the armclk speed is more than the old_rate until the dividers are
+ * set.
+ */
+ if (alt_prate > ndata->old_rate) {
+ /* calculate dividers */
+ alt_div = DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1;
+ if (alt_div > reg_data->div_core_mask) {
+ pr_warn("%s: limiting alt-divider %lu to %d\n",
+ __func__, alt_div, reg_data->div_core_mask);
+ alt_div = reg_data->div_core_mask;
+ }
+
+ /*
+ * Change parents and add dividers in a single transaction.
+ *
+ * NOTE: we do this in a single transaction so we're never
+ * dividing the primary parent by the extra dividers that were
+ * needed for the alt.
+ */
+ pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
+ __func__, alt_div, alt_prate, ndata->old_rate);
+
+ writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask,
+ reg_data->div_core_shift) |
+ HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
+ cpuclk->reg_base + reg_data->core_reg);
+ } else {
+ /* select alternate parent */
+ writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
+ cpuclk->reg_base + reg_data->core_reg);
+ }
+
+ spin_unlock(cpuclk->lock);
+ return 0;
+}
+
+static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
+ struct clk_notifier_data *ndata)
+{
+ const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
+ const struct rockchip_cpuclk_rate_table *rate;
+
+ rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for cpuclk\n",
+ __func__, ndata->new_rate);
+ return -EINVAL;
+ }
+
+ spin_lock(cpuclk->lock);
+
+ if (ndata->old_rate < ndata->new_rate)
+ rockchip_cpuclk_set_dividers(cpuclk, rate);
+
+ /*
+ * post-rate change event, re-mux to primary parent and remove dividers.
+ *
+ * NOTE: we do this in a single transaction so we're never dividing the
+ * primary parent by the extra dividers that were needed for the alt.
+ */
+
+ writel(HIWORD_UPDATE(0, reg_data->div_core_mask,
+ reg_data->div_core_shift) |
+ HIWORD_UPDATE(0, 1, reg_data->mux_core_shift),
+ cpuclk->reg_base + reg_data->core_reg);
+
+ if (ndata->old_rate > ndata->new_rate)
+ rockchip_cpuclk_set_dividers(cpuclk, rate);
+
+ spin_unlock(cpuclk->lock);
+ return 0;
+}
+
+/*
+ * This clock notifier is called when the frequency of the parent clock
+ * of cpuclk is to be changed. This notifier handles the setting up all
+ * the divider clocks, remux to temporary parent and handling the safe
+ * frequency levels when using temporary parent.
+ */
+static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
+ int ret = 0;
+
+ pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
+ __func__, event, ndata->old_rate, ndata->new_rate);
+ if (event == PRE_RATE_CHANGE)
+ ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata);
+ else if (event == POST_RATE_CHANGE)
+ ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata);
+
+ return notifier_from_errno(ret);
+}
+
+struct clk *rockchip_clk_register_cpuclk(const char *name,
+ const char **parent_names, u8 num_parents,
+ const struct rockchip_cpuclk_reg_data *reg_data,
+ const struct rockchip_cpuclk_rate_table *rates,
+ int nrates, void __iomem *reg_base, spinlock_t *lock)
+{
+ struct rockchip_cpuclk *cpuclk;
+ struct clk_init_data init;
+ struct clk *clk, *cclk;
+ int ret;
+
+ if (num_parents != 2) {
+ pr_err("%s: needs two parent clocks\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
+ if (!cpuclk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_names = &parent_names[0];
+ init.num_parents = 1;
+ init.ops = &rockchip_cpuclk_ops;
+
+ /* only allow rate changes when we have a rate table */
+ init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0;
+
+ /* disallow automatic parent changes by ccf */
+ init.flags |= CLK_SET_RATE_NO_REPARENT;
+
+ init.flags |= CLK_GET_RATE_NOCACHE;
+
+ cpuclk->reg_base = reg_base;
+ cpuclk->lock = lock;
+ cpuclk->reg_data = reg_data;
+ cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
+ cpuclk->hw.init = &init;
+
+ cpuclk->alt_parent = __clk_lookup(parent_names[1]);
+ if (!cpuclk->alt_parent) {
+ pr_err("%s: could not lookup alternate parent\n",
+ __func__);
+ ret = -EINVAL;
+ goto free_cpuclk;
+ }
+
+ ret = clk_prepare_enable(cpuclk->alt_parent);
+ if (ret) {
+ pr_err("%s: could not enable alternate parent\n",
+ __func__);
+ goto free_cpuclk;
+ }
+
+ clk = __clk_lookup(parent_names[0]);
+ if (!clk) {
+ pr_err("%s: could not lookup parent clock %s\n",
+ __func__, parent_names[0]);
+ ret = -EINVAL;
+ goto free_cpuclk;
+ }
+
+ ret = clk_notifier_register(clk, &cpuclk->clk_nb);
+ if (ret) {
+ pr_err("%s: failed to register clock notifier for %s\n",
+ __func__, name);
+ goto free_cpuclk;
+ }
+
+ if (nrates > 0) {
+ cpuclk->rate_count = nrates;
+ cpuclk->rate_table = kmemdup(rates,
+ sizeof(*rates) * nrates,
+ GFP_KERNEL);
+ if (!cpuclk->rate_table) {
+ pr_err("%s: could not allocate memory for cpuclk rates\n",
+ __func__);
+ ret = -ENOMEM;
+ goto unregister_notifier;
+ }
+ }
+
+ cclk = clk_register(NULL, &cpuclk->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not register cpuclk %s\n", __func__, name);
+ ret = PTR_ERR(clk);
+ goto free_rate_table;
+ }
+
+ return cclk;
+
+free_rate_table:
+ kfree(cpuclk->rate_table);
+unregister_notifier:
+ clk_notifier_unregister(clk, &cpuclk->clk_nb);
+free_cpuclk:
+ kfree(cpuclk);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index f2a1c7abf4d9..a3e886a38480 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -34,7 +34,6 @@ struct rockchip_clk_pll {
const struct clk_ops *pll_mux_ops;
struct notifier_block clk_nb;
- bool rate_change_remuxed;
void __iomem *reg_base;
int lock_offset;
@@ -109,38 +108,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
}
/**
- * Set pll mux when changing the pll rate.
- * This makes sure to move the pll mux away from the actual pll before
- * changing its rate and back to the original parent after the change.
- */
-static int rockchip_pll_notifier_cb(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb);
- struct clk_mux *pll_mux = &pll->pll_mux;
- const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
- int cur_parent;
-
- switch (event) {
- case PRE_RATE_CHANGE:
- cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
- if (cur_parent == PLL_MODE_NORM) {
- pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
- pll->rate_change_remuxed = 1;
- }
- break;
- case POST_RATE_CHANGE:
- if (pll->rate_change_remuxed) {
- pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
- pll->rate_change_remuxed = 0;
- }
- break;
- }
-
- return NOTIFY_OK;
-}
-
-/**
* PLL used in RK3066, RK3188 and RK3288
*/
@@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
const struct rockchip_pll_rate_table *rate;
unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
struct regmap *grf = rockchip_clk_get_grf();
+ struct clk_mux *pll_mux = &pll->pll_mux;
+ const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
+ int rate_change_remuxed = 0;
+ int cur_parent;
int ret;
if (IS_ERR(grf)) {
@@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
__func__, rate->rate, rate->nr, rate->no, rate->nf);
+ cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
+ if (cur_parent == PLL_MODE_NORM) {
+ pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
+ rate_change_remuxed = 1;
+ }
+
/* enter reset mode */
writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
pll->reg_base + RK3066_PLLCON(3));
@@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
}
+ if (rate_change_remuxed)
+ pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
+
return ret;
}
@@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
struct clk_mux *pll_mux;
struct clk *pll_clk, *mux_clk;
char pll_name[20];
- int ret;
if (num_parents != 2) {
pr_err("%s: needs two parent clocks\n", __func__);
@@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
pll->lock_offset = grf_lock_offset;
pll->lock_shift = lock_shift;
pll->lock = lock;
- pll->clk_nb.notifier_call = rockchip_pll_notifier_cb;
pll_clk = clk_register(NULL, &pll->hw);
if (IS_ERR(pll_clk)) {
@@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
goto err_pll;
}
- ret = clk_notifier_register(pll_clk, &pll->clk_nb);
- if (ret) {
- pr_err("%s: failed to register clock notifier for %s : %d\n",
- __func__, name, ret);
- mux_clk = ERR_PTR(ret);
- goto err_pll_notifier;
- }
-
/* create the mux on top of the real pll */
pll->pll_mux_ops = &clk_mux_ops;
pll_mux = &pll->pll_mux;
@@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
return mux_clk;
err_mux:
- ret = clk_notifier_unregister(pll_clk, &pll->clk_nb);
- if (ret) {
- pr_err("%s: could not unregister clock notifier in error path : %d\n",
- __func__, ret);
- return mux_clk;
- }
-err_pll_notifier:
clk_unregister(pll_clk);
err_pll:
kfree(pll);
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index a83a6d8d0fb6..beed49c79126 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -19,6 +19,7 @@
#include <dt-bindings/clock/rk3188-cru-common.h>
#include "clk.h"
+#define RK3066_GRF_SOC_STATUS 0x15c
#define RK3188_GRF_SOC_STATUS 0xac
enum rk3188_plls {
@@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = {
{ /* sentinel */ },
};
+#define RK3066_DIV_CORE_PERIPH_MASK 0x3
+#define RK3066_DIV_CORE_PERIPH_SHIFT 6
+#define RK3066_DIV_ACLK_CORE_MASK 0x7
+#define RK3066_DIV_ACLK_CORE_SHIFT 0
+#define RK3066_DIV_ACLK_HCLK_MASK 0x3
+#define RK3066_DIV_ACLK_HCLK_SHIFT 8
+#define RK3066_DIV_ACLK_PCLK_MASK 0x3
+#define RK3066_DIV_ACLK_PCLK_SHIFT 12
+#define RK3066_DIV_AHB2APB_MASK 0x3
+#define RK3066_DIV_AHB2APB_SHIFT 14
+
+#define RK3066_CLKSEL0(_core_peri) \
+ { \
+ .reg = RK2928_CLKSEL_CON(0), \
+ .val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \
+ RK3066_DIV_CORE_PERIPH_SHIFT) \
+ }
+#define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb) \
+ { \
+ .reg = RK2928_CLKSEL_CON(1), \
+ .val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \
+ RK3066_DIV_ACLK_CORE_SHIFT) | \
+ HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \
+ RK3066_DIV_ACLK_HCLK_SHIFT) | \
+ HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \
+ RK3066_DIV_ACLK_PCLK_SHIFT) | \
+ HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK, \
+ RK3066_DIV_AHB2APB_SHIFT), \
+ }
+
+#define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \
+ { \
+ .prate = _prate, \
+ .divs = { \
+ RK3066_CLKSEL0(_core_peri), \
+ RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p), \
+ }, \
+ }
+
+static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = {
+ RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1),
+ RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1),
+ RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1),
+ RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1),
+ RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1),
+ RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1),
+ RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = {
+ .core_reg = RK2928_CLKSEL_CON(0),
+ .div_core_shift = 0,
+ .div_core_mask = 0x1f,
+ .mux_core_shift = 8,
+};
+
+#define RK3188_DIV_ACLK_CORE_MASK 0x7
+#define RK3188_DIV_ACLK_CORE_SHIFT 3
+
+#define RK3188_CLKSEL1(_aclk_core) \
+ { \
+ .reg = RK2928_CLKSEL_CON(1), \
+ .val = HIWORD_UPDATE(_aclk_core, RK3188_DIV_ACLK_CORE_MASK,\
+ RK3188_DIV_ACLK_CORE_SHIFT) \
+ }
+#define RK3188_CPUCLK_RATE(_prate, _core_peri, _aclk_core) \
+ { \
+ .prate = _prate, \
+ .divs = { \
+ RK3066_CLKSEL0(_core_peri), \
+ RK3188_CLKSEL1(_aclk_core), \
+ }, \
+ }
+
+static struct rockchip_cpuclk_rate_table rk3188_cpuclk_rates[] __initdata = {
+ RK3188_CPUCLK_RATE(1608000000, 2, 3),
+ RK3188_CPUCLK_RATE(1416000000, 2, 3),
+ RK3188_CPUCLK_RATE(1200000000, 2, 3),
+ RK3188_CPUCLK_RATE(1008000000, 2, 3),
+ RK3188_CPUCLK_RATE( 816000000, 2, 3),
+ RK3188_CPUCLK_RATE( 600000000, 1, 3),
+ RK3188_CPUCLK_RATE( 504000000, 1, 3),
+ RK3188_CPUCLK_RATE( 312000000, 0, 1),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = {
+ .core_reg = RK2928_CLKSEL_CON(0),
+ .div_core_shift = 9,
+ .div_core_mask = 0x1f,
+ .mux_core_shift = 8,
+};
+
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
PNAME(mux_armclk_p) = { "apll", "gpll_armclk" };
PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" };
@@ -173,17 +266,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
GATE(0, "aclk_cpu", "aclk_cpu_pre", 0,
RK2928_CLKGATE_CON(0), 3, GFLAGS),
- DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
- RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
GATE(0, "atclk_cpu", "pclk_cpu_pre", 0,
RK2928_CLKGATE_CON(0), 6, GFLAGS),
GATE(0, "pclk_cpu", "pclk_cpu_pre", 0,
RK2928_CLKGATE_CON(0), 5, GFLAGS),
- DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
- RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
- COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
- RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
- RK2928_CLKGATE_CON(4), 9, GFLAGS),
GATE(0, "hclk_cpu", "hclk_cpu_pre", 0,
RK2928_CLKGATE_CON(0), 4, GFLAGS),
@@ -412,10 +498,18 @@ static struct clk_div_table div_aclk_cpu_t[] = {
};
static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
- COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
- RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS),
DIVTBL(0, "aclk_cpu_pre", "armclk", 0,
- RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t),
+ RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t),
+ DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
+ | CLK_DIVIDER_READ_ONLY),
+ DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
+ | CLK_DIVIDER_READ_ONLY),
+ COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
+ | CLK_DIVIDER_READ_ONLY,
+ RK2928_CLKGATE_CON(4), 9, GFLAGS),
GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0,
RK2928_CLKGATE_CON(9), 4, GFLAGS),
@@ -524,8 +618,6 @@ PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1",
"gpll", "cpll" };
static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
- COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
- RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS),
COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0,
RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS),
@@ -533,6 +625,13 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
/* do not source aclk_cpu_pre from the apll, to keep complexity down */
COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT,
RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS),
+ DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
+ DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
+ COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
+ RK2928_CLKGATE_CON(4), 9, GFLAGS),
GATE(CORE_L2C, "core_l2c", "armclk", 0,
RK2928_CLKGATE_CON(9), 4, GFLAGS),
@@ -599,6 +698,12 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
GATE(ACLK_GPS, "aclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS),
};
+static const char *rk3188_critical_clocks[] __initconst = {
+ "aclk_cpu",
+ "aclk_peri",
+ "hclk_peri",
+};
+
static void __init rk3188_common_clk_init(struct device_node *np)
{
void __iomem *reg_base;
@@ -623,29 +728,65 @@ static void __init rk3188_common_clk_init(struct device_node *np)
pr_warn("%s: could not register clock usb480m: %ld\n",
__func__, PTR_ERR(clk));
- rockchip_clk_register_plls(rk3188_pll_clks,
- ARRAY_SIZE(rk3188_pll_clks),
- RK3188_GRF_SOC_STATUS);
rockchip_clk_register_branches(common_clk_branches,
ARRAY_SIZE(common_clk_branches));
+ rockchip_clk_protect_critical(rk3188_critical_clocks,
+ ARRAY_SIZE(rk3188_critical_clocks));
rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
ROCKCHIP_SOFTRST_HIWORD_MASK);
+
+ rockchip_register_restart_notifier(RK2928_GLB_SRST_FST);
}
static void __init rk3066a_clk_init(struct device_node *np)
{
rk3188_common_clk_init(np);
+ rockchip_clk_register_plls(rk3188_pll_clks,
+ ARRAY_SIZE(rk3188_pll_clks),
+ RK3066_GRF_SOC_STATUS);
rockchip_clk_register_branches(rk3066a_clk_branches,
ARRAY_SIZE(rk3066a_clk_branches));
+ rockchip_clk_register_armclk(ARMCLK, "armclk",
+ mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
+ &rk3066_cpuclk_data, rk3066_cpuclk_rates,
+ ARRAY_SIZE(rk3066_cpuclk_rates));
}
CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
static void __init rk3188a_clk_init(struct device_node *np)
{
+ struct clk *clk1, *clk2;
+ unsigned long rate;
+ int ret;
+
rk3188_common_clk_init(np);
+ rockchip_clk_register_plls(rk3188_pll_clks,
+ ARRAY_SIZE(rk3188_pll_clks),
+ RK3188_GRF_SOC_STATUS);
rockchip_clk_register_branches(rk3188_clk_branches,
ARRAY_SIZE(rk3188_clk_branches));
+ rockchip_clk_register_armclk(ARMCLK, "armclk",
+ mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
+ &rk3188_cpuclk_data, rk3188_cpuclk_rates,
+ ARRAY_SIZE(rk3188_cpuclk_rates));
+
+ /* reparent aclk_cpu_pre from apll */
+ clk1 = __clk_lookup("aclk_cpu_pre");
+ clk2 = __clk_lookup("gpll");
+ if (clk1 && clk2) {
+ rate = clk_get_rate(clk1);
+
+ ret = clk_set_parent(clk1, clk2);
+ if (ret < 0)
+ pr_warn("%s: could not reparent aclk_cpu_pre to gpll\n",
+ __func__);
+
+ clk_set_rate(clk1, rate);
+ } else {
+ pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n",
+ __func__);
+ }
}
CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index b22a2d2f21e9..23278291da44 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -20,7 +20,7 @@
#include "clk.h"
#define RK3288_GRF_SOC_CON(x) (0x244 + x * 4)
-#define RK3288_GRF_SOC_STATUS 0x280
+#define RK3288_GRF_SOC_STATUS1 0x284
enum rk3288_plls {
apll, dpll, cpll, gpll, npll,
@@ -101,6 +101,70 @@ struct rockchip_pll_rate_table rk3288_pll_rates[] = {
{ /* sentinel */ },
};
+#define RK3288_DIV_ACLK_CORE_M0_MASK 0xf
+#define RK3288_DIV_ACLK_CORE_M0_SHIFT 0
+#define RK3288_DIV_ACLK_CORE_MP_MASK 0xf
+#define RK3288_DIV_ACLK_CORE_MP_SHIFT 4
+#define RK3288_DIV_L2RAM_MASK 0x7
+#define RK3288_DIV_L2RAM_SHIFT 0
+#define RK3288_DIV_ATCLK_MASK 0x1f
+#define RK3288_DIV_ATCLK_SHIFT 4
+#define RK3288_DIV_PCLK_DBGPRE_MASK 0x1f
+#define RK3288_DIV_PCLK_DBGPRE_SHIFT 9
+
+#define RK3288_CLKSEL0(_core_m0, _core_mp) \
+ { \
+ .reg = RK3288_CLKSEL_CON(0), \
+ .val = HIWORD_UPDATE(_core_m0, RK3288_DIV_ACLK_CORE_M0_MASK, \
+ RK3288_DIV_ACLK_CORE_M0_SHIFT) | \
+ HIWORD_UPDATE(_core_mp, RK3288_DIV_ACLK_CORE_MP_MASK, \
+ RK3288_DIV_ACLK_CORE_MP_SHIFT), \
+ }
+#define RK3288_CLKSEL37(_l2ram, _atclk, _pclk_dbg_pre) \
+ { \
+ .reg = RK3288_CLKSEL_CON(37), \
+ .val = HIWORD_UPDATE(_l2ram, RK3288_DIV_L2RAM_MASK, \
+ RK3288_DIV_L2RAM_SHIFT) | \
+ HIWORD_UPDATE(_atclk, RK3288_DIV_ATCLK_MASK, \
+ RK3288_DIV_ATCLK_SHIFT) | \
+ HIWORD_UPDATE(_pclk_dbg_pre, \
+ RK3288_DIV_PCLK_DBGPRE_MASK, \
+ RK3288_DIV_PCLK_DBGPRE_SHIFT), \
+ }
+
+#define RK3288_CPUCLK_RATE(_prate, _core_m0, _core_mp, _l2ram, _atclk, _pdbg) \
+ { \
+ .prate = _prate, \
+ .divs = { \
+ RK3288_CLKSEL0(_core_m0, _core_mp), \
+ RK3288_CLKSEL37(_l2ram, _atclk, _pdbg), \
+ }, \
+ }
+
+static struct rockchip_cpuclk_rate_table rk3288_cpuclk_rates[] __initdata = {
+ RK3288_CPUCLK_RATE(1800000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE(1704000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE(1608000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE(1512000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE(1416000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE(1200000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE(1008000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 816000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 696000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 600000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 408000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 312000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 216000000, 2, 4, 2, 4, 4),
+ RK3288_CPUCLK_RATE( 126000000, 2, 4, 2, 4, 4),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = {
+ .core_reg = RK3288_CLKSEL_CON(0),
+ .div_core_shift = 8,
+ .div_core_mask = 0x1f,
+ .mux_core_shift = 15,
+};
+
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
PNAME(mux_armclk_p) = { "apll_core", "gpll_core" };
PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" };
@@ -143,7 +207,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
RK3288_MODE_CON, 12, 8, rk3288_pll_rates),
[npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16),
- RK3288_MODE_CON, 14, 9, NULL),
+ RK3288_MODE_CON, 14, 9, rk3288_pll_rates),
};
static struct clk_div_table div_hclk_cpu_t[] = {
@@ -166,35 +230,33 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
RK3288_CLKGATE_CON(0), 1, GFLAGS),
GATE(0, "gpll_core", "gpll", 0,
RK3288_CLKGATE_CON(0), 2, GFLAGS),
- COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
- RK3288_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS),
COMPOSITE_NOMUX(0, "armcore0", "armclk", 0,
- RK3288_CLKSEL_CON(36), 0, 3, DFLAGS,
+ RK3288_CLKSEL_CON(36), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 0, GFLAGS),
COMPOSITE_NOMUX(0, "armcore1", "armclk", 0,
- RK3288_CLKSEL_CON(36), 4, 3, DFLAGS,
+ RK3288_CLKSEL_CON(36), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 1, GFLAGS),
COMPOSITE_NOMUX(0, "armcore2", "armclk", 0,
- RK3288_CLKSEL_CON(36), 8, 3, DFLAGS,
+ RK3288_CLKSEL_CON(36), 8, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 2, GFLAGS),
COMPOSITE_NOMUX(0, "armcore3", "armclk", 0,
- RK3288_CLKSEL_CON(36), 12, 3, DFLAGS,
+ RK3288_CLKSEL_CON(36), 12, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 3, GFLAGS),
COMPOSITE_NOMUX(0, "l2ram", "armclk", 0,
- RK3288_CLKSEL_CON(37), 0, 3, DFLAGS,
+ RK3288_CLKSEL_CON(37), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 4, GFLAGS),
COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0,
- RK3288_CLKSEL_CON(0), 0, 4, DFLAGS,
+ RK3288_CLKSEL_CON(0), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 5, GFLAGS),
COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0,
- RK3288_CLKSEL_CON(0), 4, 4, DFLAGS,
+ RK3288_CLKSEL_CON(0), 4, 4, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 6, GFLAGS),
COMPOSITE_NOMUX(0, "atclk", "armclk", 0,
- RK3288_CLKSEL_CON(37), 4, 5, DFLAGS,
+ RK3288_CLKSEL_CON(37), 4, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 7, GFLAGS),
COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0,
- RK3288_CLKSEL_CON(37), 9, 5, DFLAGS,
+ RK3288_CLKSEL_CON(37), 9, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3288_CLKGATE_CON(12), 8, GFLAGS),
GATE(0, "pclk_dbg", "pclk_dbg_pre", 0,
RK3288_CLKGATE_CON(12), 9, GFLAGS),
@@ -219,12 +281,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
RK3288_CLKSEL_CON(1), 15, 1, MFLAGS, 3, 5, DFLAGS),
DIV(0, "aclk_cpu_pre", "aclk_cpu_src", 0,
RK3288_CLKSEL_CON(1), 0, 3, DFLAGS),
- GATE(0, "aclk_cpu", "aclk_cpu_pre", 0,
+ GATE(ACLK_CPU, "aclk_cpu", "aclk_cpu_pre", 0,
RK3288_CLKGATE_CON(0), 3, GFLAGS),
- COMPOSITE_NOMUX(0, "pclk_cpu", "aclk_cpu_pre", 0,
+ COMPOSITE_NOMUX(PCLK_CPU, "pclk_cpu", "aclk_cpu_pre", 0,
RK3288_CLKSEL_CON(1), 12, 3, DFLAGS,
RK3288_CLKGATE_CON(0), 5, GFLAGS),
- COMPOSITE_NOMUX_DIVTBL(0, "hclk_cpu", "aclk_cpu_pre", 0,
+ COMPOSITE_NOMUX_DIVTBL(HCLK_CPU, "hclk_cpu", "aclk_cpu_pre", 0,
RK3288_CLKSEL_CON(1), 8, 2, DFLAGS, div_hclk_cpu_t,
RK3288_CLKGATE_CON(0), 4, GFLAGS),
GATE(0, "c2c_host", "aclk_cpu_src", 0,
@@ -238,15 +300,15 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0,
RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS,
RK3288_CLKGATE_CON(4), 1, GFLAGS),
- COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", 0,
+ COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(8), 0,
RK3288_CLKGATE_CON(4), 2, GFLAGS),
- MUX(0, "i2s_pre", mux_i2s_pre_p, 0,
+ MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(4), 8, 2, MFLAGS),
- COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, 0,
+ COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(4), 12, 1, MFLAGS,
RK3288_CLKGATE_CON(4), 0, GFLAGS),
- GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 0,
+ GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", CLK_SET_RATE_PARENT,
RK3288_CLKGATE_CON(4), 3, GFLAGS),
MUX(0, "spdif_src", mux_pll_src_cpll_gpll_p, 0,
@@ -296,6 +358,20 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb480m_p, 0,
RK3288_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS,
RK3288_CLKGATE_CON(3), 11, GFLAGS),
+ /*
+ * We use aclk_vdpu by default GRF_SOC_CON0[7] setting in system,
+ * so we ignore the mux and make clocks nodes as following,
+ */
+ GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vdpu", 0,
+ RK3288_CLKGATE_CON(9), 0, GFLAGS),
+ /*
+ * We introduce a virtul node of hclk_vodec_pre_v to split one clock
+ * struct with a gate and a fix divider into two node in software.
+ */
+ GATE(0, "hclk_vcodec_pre_v", "aclk_vdpu", 0,
+ RK3288_CLKGATE_CON(3), 10, GFLAGS),
+ GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0,
+ RK3288_CLKGATE_CON(9), 1, GFLAGS),
COMPOSITE(0, "aclk_vio0", mux_pll_src_cpll_gpll_usb480m_p, 0,
RK3288_CLKSEL_CON(31), 6, 2, MFLAGS, 0, 5, DFLAGS,
@@ -309,7 +385,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
COMPOSITE(0, "aclk_rga_pre", mux_pll_src_cpll_gpll_usb480m_p, 0,
RK3288_CLKSEL_CON(30), 6, 2, MFLAGS, 0, 5, DFLAGS,
RK3288_CLKGATE_CON(3), 5, GFLAGS),
- COMPOSITE(0, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0,
+ COMPOSITE(SCLK_RGA, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0,
RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS,
RK3288_CLKGATE_CON(3), 4, GFLAGS),
@@ -320,35 +396,35 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
RK3288_CLKSEL_CON(29), 6, 2, MFLAGS, 8, 8, DFLAGS,
RK3288_CLKGATE_CON(3), 3, GFLAGS),
- COMPOSITE_NODIV(0, "sclk_edp_24m", mux_edp_24m_p, 0,
+ COMPOSITE_NODIV(SCLK_EDP_24M, "sclk_edp_24m", mux_edp_24m_p, 0,
RK3288_CLKSEL_CON(28), 15, 1, MFLAGS,
RK3288_CLKGATE_CON(3), 12, GFLAGS),
- COMPOSITE(0, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(SCLK_EDP, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0,
RK3288_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 6, DFLAGS,
RK3288_CLKGATE_CON(3), 13, GFLAGS),
- COMPOSITE(0, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0,
RK3288_CLKSEL_CON(6), 6, 2, MFLAGS, 0, 6, DFLAGS,
RK3288_CLKGATE_CON(3), 14, GFLAGS),
- COMPOSITE(0, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(SCLK_ISP_JPE, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0,
RK3288_CLKSEL_CON(6), 14, 2, MFLAGS, 8, 6, DFLAGS,
RK3288_CLKGATE_CON(3), 15, GFLAGS),
- GATE(0, "sclk_hdmi_hdcp", "xin24m", 0,
+ GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0,
RK3288_CLKGATE_CON(5), 12, GFLAGS),
- GATE(0, "sclk_hdmi_cec", "xin32k", 0,
+ GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0,
RK3288_CLKGATE_CON(5), 11, GFLAGS),
- COMPOSITE(0, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(ACLK_HEVC, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0,
RK3288_CLKSEL_CON(39), 14, 2, MFLAGS, 8, 5, DFLAGS,
RK3288_CLKGATE_CON(13), 13, GFLAGS),
- DIV(0, "hclk_hevc", "aclk_hevc", 0,
+ DIV(HCLK_HEVC, "hclk_hevc", "aclk_hevc", 0,
RK3288_CLKSEL_CON(40), 12, 2, DFLAGS),
- COMPOSITE(0, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0,
RK3288_CLKSEL_CON(42), 6, 2, MFLAGS, 0, 5, DFLAGS,
RK3288_CLKGATE_CON(13), 14, GFLAGS),
- COMPOSITE(0, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0,
RK3288_CLKSEL_CON(42), 14, 2, MFLAGS, 8, 5, DFLAGS,
RK3288_CLKGATE_CON(13), 15, GFLAGS),
@@ -371,13 +447,13 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
COMPOSITE(0, "aclk_peri_src", mux_pll_src_cpll_gpll_p, 0,
RK3288_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS,
RK3288_CLKGATE_CON(2), 0, GFLAGS),
- COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_src", 0,
+ COMPOSITE_NOMUX(PCLK_PERI, "pclk_peri", "aclk_peri_src", 0,
RK3288_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
RK3288_CLKGATE_CON(2), 3, GFLAGS),
- COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_src", 0,
+ COMPOSITE_NOMUX(HCLK_PERI, "hclk_peri", "aclk_peri_src", 0,
RK3288_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
RK3288_CLKGATE_CON(2), 2, GFLAGS),
- GATE(0, "aclk_peri", "aclk_peri_src", 0,
+ GATE(ACLK_PERI, "aclk_peri", "aclk_peri_src", 0,
RK3288_CLKGATE_CON(2), 1, GFLAGS),
/*
@@ -643,34 +719,34 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
GATE(HCLK_RGA, "hclk_rga", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 1, GFLAGS),
GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 6, GFLAGS),
GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 8, GFLAGS),
- GATE(0, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS),
- GATE(0, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS),
- GATE(0, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS),
+ GATE(HCLK_VIO_AHB_ARBI, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS),
+ GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS),
+ GATE(HCLK_VIP, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS),
GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 3, GFLAGS),
GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 1, GFLAGS),
- GATE(0, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS),
- GATE(0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS),
- GATE(0, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS),
- GATE(0, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS),
- GATE(0, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS),
- GATE(0, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS),
- GATE(0, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS),
- GATE(0, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS),
+ GATE(HCLK_VIO2_H2P, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS),
+ GATE(PCLK_MIPI_DSI0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS),
+ GATE(PCLK_MIPI_DSI1, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS),
+ GATE(PCLK_MIPI_CSI, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS),
+ GATE(PCLK_LVDS_PHY, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS),
+ GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS),
+ GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS),
+ GATE(PCLK_VIO2_H2P, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS),
/* aclk_vio0 gates */
GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 5, GFLAGS),
- GATE(0, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS),
- GATE(0, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS),
- GATE(0, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS),
+ GATE(ACLK_IEP, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS),
+ GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS),
+ GATE(ACLK_VIP, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS),
/* aclk_vio1 gates */
GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 7, GFLAGS),
- GATE(0, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS),
- GATE(0, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS),
+ GATE(ACLK_ISP, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS),
+ GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS),
/* aclk_rga_pre gates */
GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 0, GFLAGS),
- GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS),
+ GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS),
/*
* Other ungrouped clocks.
@@ -680,6 +756,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
GATE(0, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS),
};
+static const char *rk3288_critical_clocks[] __initconst = {
+ "aclk_cpu",
+ "aclk_peri",
+ "hclk_peri",
+};
+
static void __init rk3288_clk_init(struct device_node *np)
{
void __iomem *reg_base;
@@ -705,13 +787,28 @@ static void __init rk3288_clk_init(struct device_node *np)
pr_warn("%s: could not register clock usb480m: %ld\n",
__func__, PTR_ERR(clk));
+ clk = clk_register_fixed_factor(NULL, "hclk_vcodec_pre",
+ "hclk_vcodec_pre_v", 0, 1, 4);
+ if (IS_ERR(clk))
+ pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n",
+ __func__, PTR_ERR(clk));
+
rockchip_clk_register_plls(rk3288_pll_clks,
ARRAY_SIZE(rk3288_pll_clks),
- RK3288_GRF_SOC_STATUS);
+ RK3288_GRF_SOC_STATUS1);
rockchip_clk_register_branches(rk3288_clk_branches,
ARRAY_SIZE(rk3288_clk_branches));
+ rockchip_clk_protect_critical(rk3288_critical_clocks,
+ ARRAY_SIZE(rk3288_critical_clocks));
- rockchip_register_softrst(np, 9, reg_base + RK3288_SOFTRST_CON(0),
+ rockchip_clk_register_armclk(ARMCLK, "armclk",
+ mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
+ &rk3288_cpuclk_data, rk3288_cpuclk_rates,
+ ARRAY_SIZE(rk3288_cpuclk_rates));
+
+ rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0),
ROCKCHIP_SOFTRST_HIWORD_MASK);
+
+ rockchip_register_restart_notifier(RK3288_GLB_SRST_FST);
}
CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 278cf9dd1e23..1e68bff481b8 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -25,6 +25,7 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
+#include <linux/reboot.h>
#include "clk.h"
/**
@@ -37,7 +38,7 @@
*
* sometimes without one of those components.
*/
-struct clk *rockchip_clk_register_branch(const char *name,
+static struct clk *rockchip_clk_register_branch(const char *name,
const char **parent_names, u8 num_parents, void __iomem *base,
int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
u8 div_shift, u8 div_width, u8 div_flags,
@@ -103,6 +104,54 @@ struct clk *rockchip_clk_register_branch(const char *name,
return clk;
}
+static struct clk *rockchip_clk_register_frac_branch(const char *name,
+ const char **parent_names, u8 num_parents, void __iomem *base,
+ int muxdiv_offset, u8 div_flags,
+ int gate_offset, u8 gate_shift, u8 gate_flags,
+ unsigned long flags, spinlock_t *lock)
+{
+ struct clk *clk;
+ struct clk_gate *gate = NULL;
+ struct clk_fractional_divider *div = NULL;
+ const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
+
+ if (gate_offset >= 0) {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->flags = gate_flags;
+ gate->reg = base + gate_offset;
+ gate->bit_idx = gate_shift;
+ gate->lock = lock;
+ gate_ops = &clk_gate_ops;
+ }
+
+ if (muxdiv_offset < 0)
+ return ERR_PTR(-EINVAL);
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->flags = div_flags;
+ div->reg = base + muxdiv_offset;
+ div->mshift = 16;
+ div->mmask = 0xffff0000;
+ div->nshift = 0;
+ div->nmask = 0xffff;
+ div->lock = lock;
+ div_ops = &clk_fractional_divider_ops;
+
+ clk = clk_register_composite(NULL, name, parent_names, num_parents,
+ NULL, NULL,
+ &div->hw, div_ops,
+ gate ? &gate->hw : NULL, gate_ops,
+ flags);
+
+ return clk;
+}
+
static DEFINE_SPINLOCK(clk_lock);
static struct clk **clk_table;
static void __iomem *reg_base;
@@ -197,8 +246,14 @@ void __init rockchip_clk_register_branches(
list->div_flags, &clk_lock);
break;
case branch_fraction_divider:
- /* unimplemented */
- continue;
+ /* keep all gates untouched for now */
+ flags |= CLK_IGNORE_UNUSED;
+
+ clk = rockchip_clk_register_frac_branch(list->name,
+ list->parent_names, list->num_parents,
+ reg_base, list->muxdiv_offset, list->div_flags,
+ list->gate_offset, list->gate_shift,
+ list->gate_flags, flags, &clk_lock);
break;
case branch_gate:
flags |= CLK_SET_RATE_PARENT;
@@ -242,3 +297,61 @@ void __init rockchip_clk_register_branches(
rockchip_clk_add_lookup(clk, list->id);
}
}
+
+void __init rockchip_clk_register_armclk(unsigned int lookup_id,
+ const char *name, const char **parent_names,
+ u8 num_parents,
+ const struct rockchip_cpuclk_reg_data *reg_data,
+ const struct rockchip_cpuclk_rate_table *rates,
+ int nrates)
+{
+ struct clk *clk;
+
+ clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
+ reg_data, rates, nrates, reg_base,
+ &clk_lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s: %ld\n",
+ __func__, name, PTR_ERR(clk));
+ return;
+ }
+
+ rockchip_clk_add_lookup(clk, lookup_id);
+}
+
+void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks)
+{
+ int i;
+
+ /* Protect the clocks that needs to stay on */
+ for (i = 0; i < nclocks; i++) {
+ struct clk *clk = __clk_lookup(clocks[i]);
+
+ if (clk)
+ clk_prepare_enable(clk);
+ }
+}
+
+static unsigned int reg_restart;
+static int rockchip_restart_notify(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ writel(0xfdb9, reg_base + reg_restart);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rockchip_restart_handler = {
+ .notifier_call = rockchip_restart_notify,
+ .priority = 128,
+};
+
+void __init rockchip_register_restart_notifier(unsigned int reg)
+{
+ int ret;
+
+ reg_restart = reg;
+ ret = register_restart_handler(&rockchip_restart_handler);
+ if (ret)
+ pr_err("%s: cannot register restart handler, %d\n",
+ __func__, ret);
+}
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 887cbdeca2aa..ca009ab0a33a 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -120,6 +120,38 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
struct rockchip_pll_rate_table *rate_table,
spinlock_t *lock);
+struct rockchip_cpuclk_clksel {
+ int reg;
+ u32 val;
+};
+
+#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 2
+struct rockchip_cpuclk_rate_table {
+ unsigned long prate;
+ struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
+};
+
+/**
+ * struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock
+ * @core_reg: register offset of the core settings register
+ * @div_core_shift: core divider offset used to divide the pll value
+ * @div_core_mask: core divider mask
+ * @mux_core_shift: offset of the core multiplexer
+ */
+struct rockchip_cpuclk_reg_data {
+ int core_reg;
+ u8 div_core_shift;
+ u32 div_core_mask;
+ int mux_core_reg;
+ u8 mux_core_shift;
+};
+
+struct clk *rockchip_clk_register_cpuclk(const char *name,
+ const char **parent_names, u8 num_parents,
+ const struct rockchip_cpuclk_reg_data *reg_data,
+ const struct rockchip_cpuclk_rate_table *rates,
+ int nrates, void __iomem *reg_base, spinlock_t *lock);
+
#define PNAME(x) static const char *x[] __initconst
enum rockchip_clk_branch_type {
@@ -329,6 +361,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
unsigned int nr_clk);
void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
unsigned int nr_pll, int grf_lock_offset);
+void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
+ const char **parent_names, u8 num_parents,
+ const struct rockchip_cpuclk_reg_data *reg_data,
+ const struct rockchip_cpuclk_rate_table *rates,
+ int nrates);
+void rockchip_clk_protect_critical(const char *clocks[], int nclocks);
+void rockchip_register_restart_notifier(unsigned int reg);
#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c
index dc85f8e7a2d7..6e6cca392082 100644
--- a/drivers/clk/samsung/clk-exynos3250.c
+++ b/drivers/clk/samsung/clk-exynos3250.c
@@ -110,7 +110,14 @@ enum exynos3250_plls {
nr_plls
};
+/* list of PLLs in DMC block to be registered */
+enum exynos3250_dmc_plls {
+ bpll, epll,
+ nr_dmc_plls
+};
+
static void __iomem *reg_base;
+static void __iomem *dmc_reg_base;
/*
* Support for CMU save/restore across system suspends
@@ -266,6 +273,7 @@ PNAME(group_sclk_cam_blk_p) = { "xxti", "xusbxti",
"none", "none", "none",
"none", "div_mpll_pre",
"mout_epll_user", "mout_vpll",
+ "none", "none", "none",
"div_cam_blk_320", };
PNAME(group_sclk_fimd0_p) = { "xxti", "xusbxti",
"m_bitclkhsdiv4_2l", "none",
@@ -353,8 +361,8 @@ static struct samsung_mux_clock mux_clks[] __initdata = {
/* SRC_FSYS */
MUX(CLK_MOUT_TSADC, "mout_tsadc", group_sclk_p, SRC_FSYS, 28, 4),
- MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 3),
- MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 3),
+ MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 4),
+ MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 4),
/* SRC_PERIL0 */
MUX(CLK_MOUT_UART1, "mout_uart1", group_sclk_p, SRC_PERIL0, 4, 4),
@@ -423,7 +431,7 @@ static struct samsung_div_clock div_clks[] __initdata = {
DIV(CLK_DIV_SPI1_ISP, "div_spi1_isp", "mout_spi1_isp", DIV_ISP, 16, 4),
DIV_F(CLK_DIV_SPI0_ISP_PRE, "div_spi0_isp_pre", "div_spi0_isp",
DIV_ISP, 8, 8, CLK_SET_RATE_PARENT, 0),
- DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 0, 4),
+ DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 4, 4),
/* DIV_FSYS0 */
DIV_F(CLK_DIV_TSADC_PRE, "div_tsadc_pre", "div_tsadc", DIV_FSYS0, 8, 8,
@@ -724,6 +732,25 @@ static struct samsung_pll_rate_table exynos3250_pll_rates[] = {
{ /* sentinel */ }
};
+/* EPLL */
+static struct samsung_pll_rate_table exynos3250_epll_rates[] = {
+ PLL_36XX_RATE(800000000, 200, 3, 1, 0),
+ PLL_36XX_RATE(288000000, 96, 2, 2, 0),
+ PLL_36XX_RATE(192000000, 128, 2, 3, 0),
+ PLL_36XX_RATE(144000000, 96, 2, 3, 0),
+ PLL_36XX_RATE( 96000000, 128, 2, 4, 0),
+ PLL_36XX_RATE( 84000000, 112, 2, 4, 0),
+ PLL_36XX_RATE( 80000004, 106, 2, 4, 43691),
+ PLL_36XX_RATE( 73728000, 98, 2, 4, 19923),
+ PLL_36XX_RATE( 67737598, 270, 3, 5, 62285),
+ PLL_36XX_RATE( 65535999, 174, 2, 5, 49982),
+ PLL_36XX_RATE( 50000000, 200, 3, 5, 0),
+ PLL_36XX_RATE( 49152002, 131, 2, 5, 4719),
+ PLL_36XX_RATE( 48000000, 128, 2, 5, 0),
+ PLL_36XX_RATE( 45158401, 180, 3, 5, 41524),
+ { /* sentinel */ }
+};
+
/* VPLL */
static struct samsung_pll_rate_table exynos3250_vpll_rates[] = {
PLL_36XX_RATE(600000000, 100, 2, 1, 0),
@@ -821,3 +848,172 @@ static void __init exynos3250_cmu_init(struct device_node *np)
samsung_clk_of_add_provider(np, ctx);
}
CLK_OF_DECLARE(exynos3250_cmu, "samsung,exynos3250-cmu", exynos3250_cmu_init);
+
+/*
+ * CMU DMC
+ */
+
+#define BPLL_LOCK 0x0118
+#define BPLL_CON0 0x0218
+#define BPLL_CON1 0x021c
+#define BPLL_CON2 0x0220
+#define SRC_DMC 0x0300
+#define DIV_DMC1 0x0504
+#define GATE_BUS_DMC0 0x0700
+#define GATE_BUS_DMC1 0x0704
+#define GATE_BUS_DMC2 0x0708
+#define GATE_BUS_DMC3 0x070c
+#define GATE_SCLK_DMC 0x0800
+#define GATE_IP_DMC0 0x0900
+#define GATE_IP_DMC1 0x0904
+#define EPLL_LOCK 0x1110
+#define EPLL_CON0 0x1114
+#define EPLL_CON1 0x1118
+#define EPLL_CON2 0x111c
+#define SRC_EPLL 0x1120
+
+/*
+ * Support for CMU save/restore across system suspends
+ */
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos3250_dmc_clk_regs;
+
+static unsigned long exynos3250_cmu_dmc_clk_regs[] __initdata = {
+ BPLL_LOCK,
+ BPLL_CON0,
+ BPLL_CON1,
+ BPLL_CON2,
+ SRC_DMC,
+ DIV_DMC1,
+ GATE_BUS_DMC0,
+ GATE_BUS_DMC1,
+ GATE_BUS_DMC2,
+ GATE_BUS_DMC3,
+ GATE_SCLK_DMC,
+ GATE_IP_DMC0,
+ GATE_IP_DMC1,
+ EPLL_LOCK,
+ EPLL_CON0,
+ EPLL_CON1,
+ EPLL_CON2,
+ SRC_EPLL,
+};
+
+static int exynos3250_dmc_clk_suspend(void)
+{
+ samsung_clk_save(dmc_reg_base, exynos3250_dmc_clk_regs,
+ ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs));
+ return 0;
+}
+
+static void exynos3250_dmc_clk_resume(void)
+{
+ samsung_clk_restore(dmc_reg_base, exynos3250_dmc_clk_regs,
+ ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs));
+}
+
+static struct syscore_ops exynos3250_dmc_clk_syscore_ops = {
+ .suspend = exynos3250_dmc_clk_suspend,
+ .resume = exynos3250_dmc_clk_resume,
+};
+
+static void exynos3250_dmc_clk_sleep_init(void)
+{
+ exynos3250_dmc_clk_regs =
+ samsung_clk_alloc_reg_dump(exynos3250_cmu_dmc_clk_regs,
+ ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs));
+ if (!exynos3250_dmc_clk_regs) {
+ pr_warn("%s: Failed to allocate sleep save data\n", __func__);
+ goto err;
+ }
+
+ register_syscore_ops(&exynos3250_dmc_clk_syscore_ops);
+ return;
+err:
+ kfree(exynos3250_dmc_clk_regs);
+}
+#else
+static inline void exynos3250_dmc_clk_sleep_init(void) { }
+#endif
+
+PNAME(mout_epll_p) = { "fin_pll", "fout_epll", };
+PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", };
+PNAME(mout_mpll_mif_p) = { "fin_pll", "sclk_mpll_mif", };
+PNAME(mout_dphy_p) = { "mout_mpll_mif", "mout_bpll", };
+
+static struct samsung_mux_clock dmc_mux_clks[] __initdata = {
+ /*
+ * NOTE: Following table is sorted by register address in ascending
+ * order and then bitfield shift in descending order, as it is done
+ * in the User's Manual. When adding new entries, please make sure
+ * that the order is preserved, to avoid merge conflicts and make
+ * further work with defined data easier.
+ */
+
+ /* SRC_DMC */
+ MUX(CLK_MOUT_MPLL_MIF, "mout_mpll_mif", mout_mpll_mif_p, SRC_DMC, 12, 1),
+ MUX(CLK_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_DMC, 10, 1),
+ MUX(CLK_MOUT_DPHY, "mout_dphy", mout_dphy_p, SRC_DMC, 8, 1),
+ MUX(CLK_MOUT_DMC_BUS, "mout_dmc_bus", mout_dphy_p, SRC_DMC, 4, 1),
+
+ /* SRC_EPLL */
+ MUX(CLK_MOUT_EPLL, "mout_epll", mout_epll_p, SRC_EPLL, 4, 1),
+};
+
+static struct samsung_div_clock dmc_div_clks[] __initdata = {
+ /*
+ * NOTE: Following table is sorted by register address in ascending
+ * order and then bitfield shift in descending order, as it is done
+ * in the User's Manual. When adding new entries, please make sure
+ * that the order is preserved, to avoid merge conflicts and make
+ * further work with defined data easier.
+ */
+
+ /* DIV_DMC1 */
+ DIV(CLK_DIV_DMC, "div_dmc", "div_dmc_pre", DIV_DMC1, 27, 3),
+ DIV(CLK_DIV_DPHY, "div_dphy", "mout_dphy", DIV_DMC1, 23, 3),
+ DIV(CLK_DIV_DMC_PRE, "div_dmc_pre", "mout_dmc_bus", DIV_DMC1, 19, 2),
+ DIV(CLK_DIV_DMCP, "div_dmcp", "div_dmcd", DIV_DMC1, 15, 3),
+ DIV(CLK_DIV_DMCD, "div_dmcd", "div_dmc", DIV_DMC1, 11, 3),
+};
+
+static struct samsung_pll_clock exynos3250_dmc_plls[nr_dmc_plls] __initdata = {
+ [bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll",
+ BPLL_LOCK, BPLL_CON0, NULL),
+ [epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll",
+ EPLL_LOCK, EPLL_CON0, NULL),
+};
+
+static void __init exynos3250_cmu_dmc_init(struct device_node *np)
+{
+ struct samsung_clk_provider *ctx;
+
+ dmc_reg_base = of_iomap(np, 0);
+ if (!dmc_reg_base)
+ panic("%s: failed to map registers\n", __func__);
+
+ ctx = samsung_clk_init(np, dmc_reg_base, NR_CLKS_DMC);
+ if (!ctx)
+ panic("%s: unable to allocate context.\n", __func__);
+
+ exynos3250_dmc_plls[bpll].rate_table = exynos3250_pll_rates;
+ exynos3250_dmc_plls[epll].rate_table = exynos3250_epll_rates;
+
+ pr_err("CLK registering epll bpll: %d, %d, %d, %d\n",
+ exynos3250_dmc_plls[bpll].rate_table[0].rate,
+ exynos3250_dmc_plls[bpll].rate_table[0].mdiv,
+ exynos3250_dmc_plls[bpll].rate_table[0].pdiv,
+ exynos3250_dmc_plls[bpll].rate_table[0].sdiv
+ );
+ samsung_clk_register_pll(ctx, exynos3250_dmc_plls,
+ ARRAY_SIZE(exynos3250_dmc_plls), dmc_reg_base);
+
+ samsung_clk_register_mux(ctx, dmc_mux_clks, ARRAY_SIZE(dmc_mux_clks));
+ samsung_clk_register_div(ctx, dmc_div_clks, ARRAY_SIZE(dmc_div_clks));
+
+ exynos3250_dmc_clk_sleep_init();
+
+ samsung_clk_of_add_provider(np, ctx);
+}
+CLK_OF_DECLARE(exynos3250_cmu_dmc, "samsung,exynos3250-cmu-dmc",
+ exynos3250_cmu_dmc_init);
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index ac163d7f5bc3..940f02837b82 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -517,7 +517,7 @@ static struct samsung_fixed_factor_clock exynos4_fixed_factor_clks[] __initdata
FFACTOR(0, "sclk_apll_div_2", "sclk_apll", 1, 2, 0),
FFACTOR(0, "fout_mpll_div_2", "fout_mpll", 1, 2, 0),
FFACTOR(0, "fout_apll_div_2", "fout_apll", 1, 2, 0),
- FFACTOR(0, "arm_clk_div_2", "arm_clk", 1, 2, 0),
+ FFACTOR(0, "arm_clk_div_2", "div_core2", 1, 2, 0),
};
static struct samsung_fixed_factor_clock exynos4210_fixed_factor_clks[] __initdata = {
@@ -535,7 +535,7 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda
static struct samsung_mux_clock exynos4_mux_clks[] __initdata = {
MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
CLK_SET_RATE_PARENT, 0, "mout_apll"),
- MUX(0, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1),
+ MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1),
MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1),
MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1),
MUX_F(CLK_MOUT_G3D1, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1,
@@ -569,7 +569,7 @@ static struct samsung_mux_clock exynos4210_mux_clks[] __initdata = {
MUX(0, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1),
MUX(0, "mout_aclk160", sclk_ampll_p4210, SRC_TOP0, 20, 1),
MUX(0, "mout_aclk133", sclk_ampll_p4210, SRC_TOP0, 24, 1),
- MUX(0, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1),
+ MUX(CLK_MOUT_MIXER, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1),
MUX(0, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1),
MUX(0, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1),
MUX(0, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1),
@@ -719,7 +719,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = {
DIV(0, "div_periph", "div_core2", DIV_CPU0, 12, 3),
DIV(0, "div_atb", "mout_core", DIV_CPU0, 16, 3),
DIV(0, "div_pclk_dbg", "div_atb", DIV_CPU0, 20, 3),
- DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3),
+ DIV(CLK_ARM_CLK, "div_core2", "div_core", DIV_CPU0, 28, 3),
DIV(0, "div_copy", "mout_hpm", DIV_CPU1, 0, 3),
DIV(0, "div_hpm", "div_copy", DIV_CPU1, 4, 3),
DIV(0, "div_clkout_cpu", "mout_clkout_cpu", CLKOUT_CMU_CPU, 8, 6),
@@ -733,8 +733,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = {
DIV(0, "div_csis0", "mout_csis0", DIV_CAM, 24, 4),
DIV(0, "div_csis1", "mout_csis1", DIV_CAM, 28, 4),
DIV(CLK_SCLK_MFC, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4),
- DIV_F(0, "div_g3d", "mout_g3d", DIV_G3D, 0, 4,
- CLK_SET_RATE_PARENT, 0),
+ DIV(CLK_SCLK_G3D, "sclk_g3d", "mout_g3d", DIV_G3D, 0, 4),
DIV(0, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4),
DIV(0, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4),
DIV(0, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4),
@@ -769,7 +768,6 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = {
DIV(0, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8),
DIV(0, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4),
DIV(0, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4),
- DIV(CLK_ARM_CLK, "arm_clk", "div_core2", DIV_CPU0, 28, 3),
DIV(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
DIV_F(0, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4,
CLK_SET_RATE_PARENT, 0),
@@ -857,8 +855,7 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = {
0),
GATE(CLK_TSI, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0),
GATE(CLK_SROMC, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0),
- GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0,
- CLK_SET_RATE_PARENT, 0),
+ GATE(CLK_G3D, "g3d", "aclk200", GATE_IP_G3D, 0, 0, 0),
GATE(CLK_PPMUG3D, "ppmug3d", "aclk200", GATE_IP_G3D, 1, 0, 0),
GATE(CLK_USB_DEVICE, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0),
GATE(CLK_ONENAND, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0),
@@ -1183,6 +1180,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = {
GATE(CLK_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13,
CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0),
GATE(CLK_G2D, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0),
+ GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", GATE_IP_DMC, 24, 0, 0),
GATE(CLK_TMU_APBIF, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0,
0),
};
@@ -1486,7 +1484,7 @@ static void __init exynos4_clk_init(struct device_node *np,
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"));
+ _get_rate("div_core2"));
}
diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c
index ce3de97e5f11..2527e39aadcf 100644
--- a/drivers/clk/samsung/clk-exynos5260.c
+++ b/drivers/clk/samsung/clk-exynos5260.c
@@ -1581,7 +1581,7 @@ struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = {
FRATE(PHYCLK_HDMI_LINK_O_TMDS_CLKHI, "phyclk_hdmi_link_o_tmds_clkhi",
NULL, CLK_IS_ROOT, 125000000),
FRATE(PHYCLK_MIPI_DPHY_4L_M_TXBYTECLKHS,
- "phyclk_mipi_dphy_4l_m_txbyteclkhs" , NULL,
+ "phyclk_mipi_dphy_4l_m_txbyte_clkhs" , NULL,
CLK_IS_ROOT, 187500000),
FRATE(PHYCLK_DPTX_PHY_O_REF_CLK_24M, "phyclk_dptx_phy_o_ref_clk_24m",
NULL, CLK_IS_ROOT, 24000000),
diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c
index 0449cc0458ed..f4f29ed6bd25 100644
--- a/drivers/clk/samsung/clk-s3c2410-dclk.c
+++ b/drivers/clk/samsung/clk-s3c2410-dclk.c
@@ -426,7 +426,6 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids);
static struct platform_driver s3c24xx_dclk_driver = {
.driver = {
.name = "s3c24xx-dclk",
- .owner = THIS_MODULE,
.pm = &s3c24xx_dclk_pm_ops,
},
.probe = s3c24xx_dclk_probe,
diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c
index 34af09f6a155..2ceedaf8ce18 100644
--- a/drivers/clk/samsung/clk-s3c2412.c
+++ b/drivers/clk/samsung/clk-s3c2412.c
@@ -14,6 +14,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
+#include <linux/reboot.h>
#include <dt-bindings/clock/s3c2412.h>
@@ -26,6 +27,7 @@
#define CLKCON 0x0c
#define CLKDIVN 0x14
#define CLKSRC 0x1c
+#define SWRST 0x30
/* list of PLLs to be registered */
enum s3c2412_plls {
@@ -204,6 +206,28 @@ struct samsung_clock_alias s3c2412_aliases[] __initdata = {
ALIAS(MSYSCLK, NULL, "fclk"),
};
+static int s3c2412_restart(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ /* errata "Watch-dog/Software Reset Problem" specifies that
+ * this reset must be done with the SYSCLK sourced from
+ * EXTCLK instead of FOUT to avoid a glitch in the reset
+ * mechanism.
+ *
+ * See the watchdog section of the S3C2412 manual for more
+ * information on this fix.
+ */
+
+ __raw_writel(0x00, reg_base + CLKSRC);
+ __raw_writel(0x533C2412, reg_base + SWRST);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block s3c2412_restart_handler = {
+ .notifier_call = s3c2412_restart,
+ .priority = 129,
+};
+
/*
* fixed rate clocks generated outside the soc
* Only necessary until the devicetree-move is complete
@@ -233,6 +257,7 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
unsigned long ext_f, void __iomem *base)
{
struct samsung_clk_provider *ctx;
+ int ret;
reg_base = base;
if (np) {
@@ -267,6 +292,10 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
s3c2412_clk_sleep_init();
samsung_clk_of_add_provider(np, ctx);
+
+ ret = register_restart_handler(&s3c2412_restart_handler);
+ if (ret)
+ pr_warn("cannot register restart handler, %d\n", ret);
}
static void __init s3c2412_clk_init(struct device_node *np)
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
index c92f853fca9f..0c3c182b902a 100644
--- a/drivers/clk/samsung/clk-s3c2443.c
+++ b/drivers/clk/samsung/clk-s3c2443.c
@@ -14,6 +14,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
+#include <linux/reboot.h>
#include <dt-bindings/clock/s3c2443.h>
@@ -33,6 +34,7 @@
#define HCLKCON 0x30
#define PCLKCON 0x34
#define SCLKCON 0x38
+#define SWRST 0x44
/* the soc types */
enum supported_socs {
@@ -354,6 +356,18 @@ struct samsung_clock_alias s3c2450_aliases[] __initdata = {
ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"),
};
+static int s3c2443_restart(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ __raw_writel(0x533c2443, reg_base + SWRST);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block s3c2443_restart_handler = {
+ .notifier_call = s3c2443_restart,
+ .priority = 129,
+};
+
/*
* fixed rate clocks generated outside the soc
* Only necessary until the devicetree-move is complete
@@ -378,6 +392,7 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
void __iomem *base)
{
struct samsung_clk_provider *ctx;
+ int ret;
reg_base = base;
if (np) {
@@ -447,6 +462,10 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
s3c2443_clk_sleep_init();
samsung_clk_of_add_provider(np, ctx);
+
+ ret = register_restart_handler(&s3c2443_restart_handler);
+ if (ret)
+ pr_warn("cannot register restart handler, %d\n", ret);
}
static void __init s3c2416_clk_init(struct device_node *np)
diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c
index dff7f79a19b9..e996425d06a9 100644
--- a/drivers/clk/shmobile/clk-rcar-gen2.c
+++ b/drivers/clk/shmobile/clk-rcar-gen2.c
@@ -202,6 +202,7 @@ static const struct clk_div_table cpg_sdh_div_table[] = {
};
static const struct clk_div_table cpg_sd01_div_table[] = {
+ { 4, 8 },
{ 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 },
{ 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 },
};
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 6850cba35871..7ddc2b553846 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -5,6 +5,8 @@
obj-y += clk-sunxi.o clk-factors.o
obj-y += clk-a10-hosc.o
obj-y += clk-a20-gmac.o
+obj-y += clk-mod0.o
+obj-y += clk-sun8i-mbus.o
obj-$(CONFIG_MFD_SUN6I_PRCM) += \
clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
index 2057c8ac648f..f83ba097126c 100644
--- a/drivers/clk/sunxi/clk-factors.c
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -9,18 +9,18 @@
*/
#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_address.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
+ * DOC: basic adjustable factor-based clock
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
@@ -32,6 +32,8 @@
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+#define FACTORS_MAX_PARENTS 5
+
#define SETMASK(len, pos) (((1U << (len)) - 1) << (pos))
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
@@ -147,9 +149,96 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-const struct clk_ops clk_factors_ops = {
+static const struct clk_ops clk_factors_ops = {
.determine_rate = clk_factors_determine_rate,
.recalc_rate = clk_factors_recalc_rate,
.round_rate = clk_factors_round_rate,
.set_rate = clk_factors_set_rate,
};
+
+struct clk * __init sunxi_factors_register(struct device_node *node,
+ const struct factors_data *data,
+ spinlock_t *lock)
+{
+ struct clk *clk;
+ struct clk_factors *factors;
+ struct clk_gate *gate = NULL;
+ struct clk_mux *mux = NULL;
+ struct clk_hw *gate_hw = NULL;
+ struct clk_hw *mux_hw = NULL;
+ const char *clk_name = node->name;
+ const char *parents[FACTORS_MAX_PARENTS];
+ void __iomem *reg;
+ int i = 0;
+
+ reg = of_iomap(node, 0);
+
+ /* if we have a mux, we will have >1 parents */
+ while (i < FACTORS_MAX_PARENTS &&
+ (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ /*
+ * some factor clocks, such as pll5 and pll6, may have multiple
+ * outputs, and have their name designated in factors_data
+ */
+ if (data->name)
+ clk_name = data->name;
+ else
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
+ if (!factors)
+ return NULL;
+
+ /* set up factors properties */
+ factors->reg = reg;
+ factors->config = data->table;
+ factors->get_factors = data->getter;
+ factors->lock = lock;
+
+ /* Add a gate if this factor clock can be gated */
+ if (data->enable) {
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(factors);
+ return NULL;
+ }
+
+ /* set up gate properties */
+ gate->reg = reg;
+ gate->bit_idx = data->enable;
+ gate->lock = factors->lock;
+ gate_hw = &gate->hw;
+ }
+
+ /* Add a mux if this factor clock can be muxed */
+ if (data->mux) {
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux) {
+ kfree(factors);
+ kfree(gate);
+ return NULL;
+ }
+
+ /* set up gate properties */
+ mux->reg = reg;
+ mux->shift = data->mux;
+ mux->mask = SUNXI_FACTORS_MUX_MASK;
+ mux->lock = factors->lock;
+ mux_hw = &mux->hw;
+ }
+
+ clk = clk_register_composite(NULL, clk_name,
+ parents, i,
+ mux_hw, &clk_mux_ops,
+ &factors->hw, &clk_factors_ops,
+ gate_hw, &clk_gate_ops, 0);
+
+ if (!IS_ERR(clk)) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
index d2d0efa39379..9913840018d3 100644
--- a/drivers/clk/sunxi/clk-factors.h
+++ b/drivers/clk/sunxi/clk-factors.h
@@ -3,9 +3,12 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/spinlock.h>
#define SUNXI_FACTORS_NOT_APPLICABLE (0)
+#define SUNXI_FACTORS_MUX_MASK 0x3
+
struct clk_factors_config {
u8 nshift;
u8 nwidth;
@@ -18,6 +21,14 @@ struct clk_factors_config {
u8 n_start;
};
+struct factors_data {
+ int enable;
+ int mux;
+ struct clk_factors_config *table;
+ void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+ const char *name;
+};
+
struct clk_factors {
struct clk_hw hw;
void __iomem *reg;
@@ -26,5 +37,8 @@ struct clk_factors {
spinlock_t *lock;
};
-extern const struct clk_ops clk_factors_ops;
+struct clk * __init sunxi_factors_register(struct device_node *node,
+ const struct factors_data *data,
+ spinlock_t *lock);
+
#endif
diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c
new file mode 100644
index 000000000000..4a563850ee6e
--- /dev/null
+++ b/drivers/clk/sunxi/clk-mod0.c
@@ -0,0 +1,283 @@
+/*
+ * 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/of_address.h>
+
+#include "clk-factors.h"
+
+/**
+ * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
+ * MOD0 rate is calculated as follows
+ * rate = (parent_rate >> p) / (m + 1);
+ */
+
+static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p)
+{
+ u8 div, calcm, calcp;
+
+ /* These clocks can only divide, so we will never be able to achieve
+ * frequencies higher than the parent frequency */
+ if (*freq > parent_rate)
+ *freq = parent_rate;
+
+ div = DIV_ROUND_UP(parent_rate, *freq);
+
+ if (div < 16)
+ calcp = 0;
+ else if (div / 2 < 16)
+ calcp = 1;
+ else if (div / 4 < 16)
+ calcp = 2;
+ else
+ calcp = 3;
+
+ calcm = DIV_ROUND_UP(div, 1 << calcp);
+
+ *freq = (parent_rate >> calcp) / calcm;
+
+ /* we were called to round the frequency, we can now return */
+ if (n == NULL)
+ return;
+
+ *m = calcm - 1;
+ *p = calcp;
+}
+
+/* user manual says "n" but it's really "p" */
+static struct clk_factors_config sun4i_a10_mod0_config = {
+ .mshift = 0,
+ .mwidth = 4,
+ .pshift = 16,
+ .pwidth = 2,
+};
+
+static const struct factors_data sun4i_a10_mod0_data __initconst = {
+ .enable = 31,
+ .mux = 24,
+ .table = &sun4i_a10_mod0_config,
+ .getter = sun4i_a10_get_mod0_factors,
+};
+
+static DEFINE_SPINLOCK(sun4i_a10_mod0_lock);
+
+static void __init sun4i_a10_mod0_setup(struct device_node *node)
+{
+ sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock);
+}
+CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup);
+
+static DEFINE_SPINLOCK(sun5i_a13_mbus_lock);
+
+static void __init sun5i_a13_mbus_setup(struct device_node *node)
+{
+ struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock);
+
+ /* The MBUS clocks needs to be always enabled */
+ __clk_get(mbus);
+ clk_prepare_enable(mbus);
+}
+CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup);
+
+struct mmc_phase_data {
+ u8 offset;
+};
+
+struct mmc_phase {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct mmc_phase_data *data;
+ spinlock_t *lock;
+};
+
+#define to_mmc_phase(_hw) container_of(_hw, struct mmc_phase, hw)
+
+static int mmc_get_phase(struct clk_hw *hw)
+{
+ struct clk *mmc, *mmc_parent, *clk = hw->clk;
+ struct mmc_phase *phase = to_mmc_phase(hw);
+ unsigned int mmc_rate, mmc_parent_rate;
+ u16 step, mmc_div;
+ u32 value;
+ u8 delay;
+
+ value = readl(phase->reg);
+ delay = (value >> phase->data->offset) & 0x3;
+
+ if (!delay)
+ return 180;
+
+ /* Get the main MMC clock */
+ mmc = clk_get_parent(clk);
+ if (!mmc)
+ return -EINVAL;
+
+ /* And its rate */
+ mmc_rate = clk_get_rate(mmc);
+ if (!mmc_rate)
+ return -EINVAL;
+
+ /* Now, get the MMC parent (most likely some PLL) */
+ mmc_parent = clk_get_parent(mmc);
+ if (!mmc_parent)
+ return -EINVAL;
+
+ /* And its rate */
+ mmc_parent_rate = clk_get_rate(mmc_parent);
+ if (!mmc_parent_rate)
+ return -EINVAL;
+
+ /* Get MMC clock divider */
+ mmc_div = mmc_parent_rate / mmc_rate;
+
+ step = DIV_ROUND_CLOSEST(360, mmc_div);
+ return delay * step;
+}
+
+static int mmc_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk *mmc, *mmc_parent, *clk = hw->clk;
+ struct mmc_phase *phase = to_mmc_phase(hw);
+ unsigned int mmc_rate, mmc_parent_rate;
+ unsigned long flags;
+ u32 value;
+ u8 delay;
+
+ /* Get the main MMC clock */
+ mmc = clk_get_parent(clk);
+ if (!mmc)
+ return -EINVAL;
+
+ /* And its rate */
+ mmc_rate = clk_get_rate(mmc);
+ if (!mmc_rate)
+ return -EINVAL;
+
+ /* Now, get the MMC parent (most likely some PLL) */
+ mmc_parent = clk_get_parent(mmc);
+ if (!mmc_parent)
+ return -EINVAL;
+
+ /* And its rate */
+ mmc_parent_rate = clk_get_rate(mmc_parent);
+ if (!mmc_parent_rate)
+ return -EINVAL;
+
+ if (degrees != 180) {
+ u16 step, mmc_div;
+
+ /* Get MMC clock divider */
+ mmc_div = mmc_parent_rate / mmc_rate;
+
+ /*
+ * We can only outphase the clocks by multiple of the
+ * PLL's period.
+ *
+ * Since the MMC clock in only a divider, and the
+ * formula to get the outphasing in degrees is deg =
+ * 360 * delta / period
+ *
+ * If we simplify this formula, we can see that the
+ * only thing that we're concerned about is the number
+ * of period we want to outphase our clock from, and
+ * the divider set by the MMC clock.
+ */
+ step = DIV_ROUND_CLOSEST(360, mmc_div);
+ delay = DIV_ROUND_CLOSEST(degrees, step);
+ } else {
+ delay = 0;
+ }
+
+ spin_lock_irqsave(phase->lock, flags);
+ value = readl(phase->reg);
+ value &= ~GENMASK(phase->data->offset + 3, phase->data->offset);
+ value |= delay << phase->data->offset;
+ writel(value, phase->reg);
+ spin_unlock_irqrestore(phase->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops mmc_clk_ops = {
+ .get_phase = mmc_get_phase,
+ .set_phase = mmc_set_phase,
+};
+
+static void __init sun4i_a10_mmc_phase_setup(struct device_node *node,
+ struct mmc_phase_data *data)
+{
+ const char *parent_names[1] = { of_clk_get_parent_name(node, 0) };
+ struct clk_init_data init = {
+ .num_parents = 1,
+ .parent_names = parent_names,
+ .ops = &mmc_clk_ops,
+ };
+
+ struct mmc_phase *phase;
+ struct clk *clk;
+
+ phase = kmalloc(sizeof(*phase), GFP_KERNEL);
+ if (!phase)
+ return;
+
+ phase->hw.init = &init;
+
+ phase->reg = of_iomap(node, 0);
+ if (!phase->reg)
+ goto err_free;
+
+ phase->data = data;
+ phase->lock = &sun4i_a10_mod0_lock;
+
+ if (of_property_read_string(node, "clock-output-names", &init.name))
+ init.name = node->name;
+
+ clk = clk_register(NULL, &phase->hw);
+ if (IS_ERR(clk))
+ goto err_unmap;
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ return;
+
+err_unmap:
+ iounmap(phase->reg);
+err_free:
+ kfree(phase);
+}
+
+
+static struct mmc_phase_data mmc_output_clk = {
+ .offset = 8,
+};
+
+static struct mmc_phase_data mmc_sample_clk = {
+ .offset = 20,
+};
+
+static void __init sun4i_a10_mmc_output_setup(struct device_node *node)
+{
+ sun4i_a10_mmc_phase_setup(node, &mmc_output_clk);
+}
+CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup);
+
+static void __init sun4i_a10_mmc_sample_setup(struct device_node *node)
+{
+ sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk);
+}
+CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup);
diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
index e10d0521ec76..64f3e46d383c 100644
--- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
+++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
@@ -99,7 +99,6 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev)
static struct platform_driver sun6i_a31_apb0_gates_clk_driver = {
.driver = {
.name = "sun6i-a31-apb0-gates-clk",
- .owner = THIS_MODULE,
.of_match_table = sun6i_a31_apb0_gates_clk_dt_ids,
},
.probe = sun6i_a31_apb0_gates_clk_probe,
diff --git a/drivers/clk/sunxi/clk-sun6i-apb0.c b/drivers/clk/sunxi/clk-sun6i-apb0.c
index 1fa23371c8c6..70763600aeae 100644
--- a/drivers/clk/sunxi/clk-sun6i-apb0.c
+++ b/drivers/clk/sunxi/clk-sun6i-apb0.c
@@ -65,7 +65,6 @@ static const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = {
static struct platform_driver sun6i_a31_apb0_clk_driver = {
.driver = {
.name = "sun6i-a31-apb0-clk",
- .owner = THIS_MODULE,
.of_match_table = sun6i_a31_apb0_clk_dt_ids,
},
.probe = sun6i_a31_apb0_clk_probe,
diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c
index eca8ca025b6a..acca53290be2 100644
--- a/drivers/clk/sunxi/clk-sun6i-ar100.c
+++ b/drivers/clk/sunxi/clk-sun6i-ar100.c
@@ -221,7 +221,6 @@ static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
static struct platform_driver sun6i_a31_ar100_clk_driver = {
.driver = {
.name = "sun6i-a31-ar100-clk",
- .owner = THIS_MODULE,
.of_match_table = sun6i_a31_ar100_clk_dt_ids,
},
.probe = sun6i_a31_ar100_clk_probe,
diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c
index 1f5ba9b4b8cd..155d0022194f 100644
--- a/drivers/clk/sunxi/clk-sun8i-apb0.c
+++ b/drivers/clk/sunxi/clk-sun8i-apb0.c
@@ -56,7 +56,6 @@ static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = {
static struct platform_driver sun8i_a23_apb0_clk_driver = {
.driver = {
.name = "sun8i-a23-apb0-clk",
- .owner = THIS_MODULE,
.of_match_table = sun8i_a23_apb0_clk_dt_ids,
},
.probe = sun8i_a23_apb0_clk_probe,
diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c
new file mode 100644
index 000000000000..8e49b44cee41
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun8i-mbus.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+
+#include "clk-factors.h"
+
+/**
+ * sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks
+ * MBUS rate is calculated as follows
+ * rate = parent_rate / (m + 1);
+ */
+
+static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p)
+{
+ u8 div;
+
+ /*
+ * These clocks can only divide, so we will never be able to
+ * achieve frequencies higher than the parent frequency
+ */
+ if (*freq > parent_rate)
+ *freq = parent_rate;
+
+ div = DIV_ROUND_UP(parent_rate, *freq);
+
+ if (div > 8)
+ div = 8;
+
+ *freq = parent_rate / div;
+
+ /* we were called to round the frequency, we can now return */
+ if (m == NULL)
+ return;
+
+ *m = div - 1;
+}
+
+static struct clk_factors_config sun8i_a23_mbus_config = {
+ .mshift = 0,
+ .mwidth = 3,
+};
+
+static const struct factors_data sun8i_a23_mbus_data __initconst = {
+ .enable = 31,
+ .mux = 24,
+ .table = &sun8i_a23_mbus_config,
+ .getter = sun8i_a23_get_mbus_factors,
+};
+
+static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
+
+static void __init sun8i_a23_mbus_setup(struct device_node *node)
+{
+ struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
+ &sun8i_a23_mbus_lock);
+
+ /* The MBUS clocks needs to be always enabled */
+ __clk_get(mbus);
+ clk_prepare_enable(mbus);
+}
+CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup);
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index b654b7b1d137..d5dc951264ca 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
#include "clk-factors.h"
@@ -319,46 +320,6 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
-/**
- * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
- * MOD0 rate is calculated as follows
- * rate = (parent_rate >> p) / (m + 1);
- */
-
-static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
-{
- u8 div, calcm, calcp;
-
- /* These clocks can only divide, so we will never be able to achieve
- * frequencies higher than the parent frequency */
- if (*freq > parent_rate)
- *freq = parent_rate;
-
- div = DIV_ROUND_UP(parent_rate, *freq);
-
- if (div < 16)
- calcp = 0;
- else if (div / 2 < 16)
- calcp = 1;
- else if (div / 4 < 16)
- calcp = 2;
- else
- calcp = 3;
-
- calcm = DIV_ROUND_UP(div, 1 << calcp);
-
- *freq = (parent_rate >> calcp) / calcm;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
-
- *m = calcm - 1;
- *p = calcp;
-}
-
-
/**
* sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B
@@ -440,16 +401,6 @@ EXPORT_SYMBOL(clk_sunxi_mmc_phase_control);
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
-#define SUNXI_FACTORS_MUX_MASK 0x3
-
-struct factors_data {
- int enable;
- int mux;
- struct clk_factors_config *table;
- void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
- const char *name;
-};
-
static struct clk_factors_config sun4i_pll1_config = {
.nshift = 8,
.nwidth = 5,
@@ -504,14 +455,6 @@ static struct clk_factors_config sun4i_apb1_config = {
};
/* user manual says "n" but it's really "p" */
-static struct clk_factors_config sun4i_mod0_config = {
- .mshift = 0,
- .mwidth = 4,
- .pshift = 16,
- .pwidth = 2,
-};
-
-/* user manual says "n" but it's really "p" */
static struct clk_factors_config sun7i_a20_out_config = {
.mshift = 8,
.mwidth = 5,
@@ -568,13 +511,6 @@ static const struct factors_data sun4i_apb1_data __initconst = {
.getter = sun4i_get_apb1_factors,
};
-static const struct factors_data sun4i_mod0_data __initconst = {
- .enable = 31,
- .mux = 24,
- .table = &sun4i_mod0_config,
- .getter = sun4i_get_mod0_factors,
-};
-
static const struct factors_data sun7i_a20_out_data __initconst = {
.enable = 31,
.mux = 24,
@@ -583,89 +519,9 @@ static const struct factors_data sun7i_a20_out_data __initconst = {
};
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
- const struct factors_data *data)
+ const struct factors_data *data)
{
- struct clk *clk;
- struct clk_factors *factors;
- struct clk_gate *gate = NULL;
- struct clk_mux *mux = NULL;
- struct clk_hw *gate_hw = NULL;
- struct clk_hw *mux_hw = NULL;
- const char *clk_name = node->name;
- const char *parents[SUNXI_MAX_PARENTS];
- void __iomem *reg;
- int i = 0;
-
- reg = of_iomap(node, 0);
-
- /* if we have a mux, we will have >1 parents */
- while (i < SUNXI_MAX_PARENTS &&
- (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
- i++;
-
- /*
- * some factor clocks, such as pll5 and pll6, may have multiple
- * outputs, and have their name designated in factors_data
- */
- if (data->name)
- clk_name = data->name;
- else
- of_property_read_string(node, "clock-output-names", &clk_name);
-
- factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
- if (!factors)
- return NULL;
-
- /* Add a gate if this factor clock can be gated */
- if (data->enable) {
- gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
- if (!gate) {
- kfree(factors);
- return NULL;
- }
-
- /* set up gate properties */
- gate->reg = reg;
- gate->bit_idx = data->enable;
- gate->lock = &clk_lock;
- gate_hw = &gate->hw;
- }
-
- /* Add a mux if this factor clock can be muxed */
- if (data->mux) {
- mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
- if (!mux) {
- kfree(factors);
- kfree(gate);
- return NULL;
- }
-
- /* set up gate properties */
- mux->reg = reg;
- mux->shift = data->mux;
- mux->mask = SUNXI_FACTORS_MUX_MASK;
- mux->lock = &clk_lock;
- mux_hw = &mux->hw;
- }
-
- /* set up factors properties */
- factors->reg = reg;
- factors->config = data->table;
- factors->get_factors = data->getter;
- factors->lock = &clk_lock;
-
- clk = clk_register_composite(NULL, clk_name,
- parents, i,
- mux_hw, &clk_mux_ops,
- &factors->hw, &clk_factors_ops,
- gate_hw, &clk_gate_ops, 0);
-
- if (!IS_ERR(clk)) {
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
- }
-
- return clk;
+ return sunxi_factors_register(node, data, &clk_lock);
}
@@ -762,10 +618,19 @@ static const struct div_data sun4i_ahb_data __initconst = {
.width = 2,
};
+static const struct clk_div_table sun4i_apb0_table[] __initconst = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { } /* sentinel */
+};
+
static const struct div_data sun4i_apb0_data __initconst = {
.shift = 8,
.pow = 1,
.width = 2,
+ .table = sun4i_apb0_table,
};
static const struct div_data sun6i_a31_apb2_div_data __initconst = {
@@ -1199,7 +1064,6 @@ static const struct of_device_id clk_factors_match[] __initconst = {
{.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
{.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
{.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
- {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
{}
};
@@ -1311,7 +1175,6 @@ static void __init sun4i_a10_init_clocks(struct device_node *node)
CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks);
static const char *sun5i_critical_clocks[] __initdata = {
- "mbus",
"pll5_ddr",
"ahb_sdram",
};
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 9525c684d149..e3a85842ce0c 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -1166,6 +1166,12 @@ static void __init tegra124_pll_init(void __iomem *clk_base,
clk_register_clkdev(clk, "pll_c_out1", NULL);
clks[TEGRA124_CLK_PLL_C_OUT1] = clk;
+ /* PLLC_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_c_ud", "pll_c",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_c_ud", NULL);
+ clks[TEGRA124_CLK_PLL_C_UD] = clk;
+
/* PLLC2 */
clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0,
&pll_c2_params, NULL);
@@ -1198,6 +1204,8 @@ static void __init tegra124_pll_init(void __iomem *clk_base,
/* PLLM_UD */
clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m",
CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_m_ud", NULL);
+ clks[TEGRA124_CLK_PLL_M_UD] = clk;
/* PLLU */
val = readl(clk_base + pll_u_params.base_reg);
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index f87c609e8f72..97dc8595c3cd 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -207,8 +207,13 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
for (; tbl->clk_id < clk_max; tbl++) {
clk = clks[tbl->clk_id];
- if (IS_ERR_OR_NULL(clk))
- return;
+ if (IS_ERR_OR_NULL(clk)) {
+ pr_err("%s: invalid entry %ld in clks array for id %d\n",
+ __func__, PTR_ERR(clk), tbl->clk_id);
+ WARN_ON(1);
+
+ continue;
+ }
if (tbl->parent_id < clk_max) {
struct clk *parent = clks[tbl->parent_id];
diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c
index af29359677da..59bb4b39d12e 100644
--- a/drivers/clk/ti/clk-dra7-atl.c
+++ b/drivers/clk/ti/clk-dra7-atl.c
@@ -203,6 +203,7 @@ static void __init of_dra7_atl_clock_setup(struct device_node *node)
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ kfree(parent_names);
return;
}
cleanup:
@@ -228,6 +229,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev)
cinfo->iobase = of_iomap(node, 0);
cinfo->dev = &pdev->dev;
pm_runtime_enable(cinfo->dev);
+ pm_runtime_irq_safe(cinfo->dev);
pm_runtime_get_sync(cinfo->dev);
atl_write(cinfo, DRA7_ATL_PCLKMUX_REG(0), DRA7_ATL_PCLKMUX);
@@ -301,7 +303,6 @@ MODULE_DEVICE_TABLE(of, of_dra7_atl_clk_match_tbl);
static struct platform_driver dra7_atl_clk_driver = {
.driver = {
.name = "dra7-atl",
- .owner = THIS_MODULE,
.of_match_table = of_dra7_atl_clk_match_tbl,
},
.probe = of_dra7_atl_clk_probe,
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c
index b1a6f7144f3f..337abe5909e1 100644
--- a/drivers/clk/ti/clk.c
+++ b/drivers/clk/ti/clk.c
@@ -25,8 +25,8 @@
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
-static int ti_dt_clk_memmap_index;
struct ti_clk_ll_ops *ti_clk_ll_ops;
+static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS];
/**
* ti_dt_clocks_register - register DT alias clocks during boot
@@ -108,9 +108,21 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index)
struct clk_omap_reg *reg;
u32 val;
u32 tmp;
+ int i;
reg = (struct clk_omap_reg *)&tmp;
- reg->index = ti_dt_clk_memmap_index;
+
+ for (i = 0; i < CLK_MAX_MEMMAPS; i++) {
+ if (clocks_node_ptr[i] == node->parent)
+ break;
+ }
+
+ if (i == CLK_MAX_MEMMAPS) {
+ pr_err("clk-provider not found for %s!\n", node->name);
+ return NULL;
+ }
+
+ reg->index = i;
if (of_property_read_u32_index(node, "reg", index, &val)) {
pr_err("%s must have reg[%d]!\n", node->name, index);
@@ -127,20 +139,14 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index)
* @parent: master node
* @index: internal index for clk_reg_ops
*
- * Initializes a master clock IP block and its child clock nodes.
- * Regmap is provided for accessing the register space for the
- * IP block and all the clocks under it.
+ * Initializes a master clock IP block. This basically sets up the
+ * mapping from clocks node to the memory map index. All the clocks
+ * are then initialized through the common of_clk_init call, and the
+ * clocks will access their memory maps based on the node layout.
*/
void ti_dt_clk_init_provider(struct device_node *parent, int index)
{
- const struct of_device_id *match;
- struct device_node *np;
struct device_node *clocks;
- of_clk_init_cb_t clk_init_cb;
- struct clk_init_item *retry;
- struct clk_init_item *tmp;
-
- ti_dt_clk_memmap_index = index;
/* get clocks for this parent */
clocks = of_get_child_by_name(parent, "clocks");
@@ -149,19 +155,31 @@ void ti_dt_clk_init_provider(struct device_node *parent, int index)
return;
}
- for_each_child_of_node(clocks, np) {
- match = of_match_node(&__clk_of_table, np);
- if (!match)
- continue;
- clk_init_cb = (of_clk_init_cb_t)match->data;
- pr_debug("%s: initializing: %s\n", __func__, np->name);
- clk_init_cb(np);
- }
+ /* add clocks node info */
+ clocks_node_ptr[index] = clocks;
+}
- list_for_each_entry_safe(retry, tmp, &retry_list, link) {
- pr_debug("retry-init: %s\n", retry->node->name);
- retry->func(retry->hw, retry->node);
- list_del(&retry->link);
- kfree(retry);
+/**
+ * ti_dt_clk_init_retry_clks - init clocks from the retry list
+ *
+ * Initializes any clocks that have failed to initialize before,
+ * reasons being missing parent node(s) during earlier init. This
+ * typically happens only for DPLLs which need to have both of their
+ * parent clocks ready during init.
+ */
+void ti_dt_clk_init_retry_clks(void)
+{
+ struct clk_init_item *retry;
+ struct clk_init_item *tmp;
+ int retries = 5;
+
+ while (!list_empty(&retry_list) && retries) {
+ list_for_each_entry_safe(retry, tmp, &retry_list, link) {
+ pr_debug("retry-init: %s\n", retry->node->name);
+ retry->func(retry->hw, retry->node);
+ list_del(&retry->link);
+ kfree(retry);
+ }
+ retries--;
}
}
diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c
index f1e0038d76ac..b4c5faccaece 100644
--- a/drivers/clk/ti/clockdomain.c
+++ b/drivers/clk/ti/clockdomain.c
@@ -36,6 +36,11 @@ static void __init of_ti_clockdomain_setup(struct device_node *node)
for (i = 0; i < num_clks; i++) {
clk = of_clk_get(node, i);
+ if (IS_ERR(clk)) {
+ pr_err("%s: Failed get %s' clock nr %d (%ld)\n",
+ __func__, node->full_name, i, PTR_ERR(clk));
+ continue;
+ }
if (__clk_get_flags(clk) & CLK_IS_BASIC) {
pr_warn("can't setup clkdm for basic clk %s\n",
__clk_get_name(clk));
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index a837f703be65..bff2b5b8ff59 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -300,8 +300,8 @@ static struct clk *_register_divider(struct device *dev, const char *name,
return clk;
}
-static struct clk_div_table
-__init *ti_clk_get_div_table(struct device_node *node)
+static struct clk_div_table *
+__init ti_clk_get_div_table(struct device_node *node)
{
struct clk_div_table *table;
const __be32 *divspec;
diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c
index 246cf1226eaa..9037bebd69f7 100644
--- a/drivers/clk/zynq/clkc.c
+++ b/drivers/clk/zynq/clkc.c
@@ -85,24 +85,22 @@ static DEFINE_SPINLOCK(canmioclk_lock);
static DEFINE_SPINLOCK(dbgclk_lock);
static DEFINE_SPINLOCK(aperclk_lock);
-static const char dummy_nm[] __initconst = "dummy_name";
-
-static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"};
-static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"};
-static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"};
-static const char *gem0_mux_parents[] __initdata = {"gem0_div1", dummy_nm};
-static const char *gem1_mux_parents[] __initdata = {"gem1_div1", dummy_nm};
-static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate",
+static const char *armpll_parents[] __initconst = {"armpll_int", "ps_clk"};
+static const char *ddrpll_parents[] __initconst = {"ddrpll_int", "ps_clk"};
+static const char *iopll_parents[] __initconst = {"iopll_int", "ps_clk"};
+static const char *gem0_mux_parents[] __initconst = {"gem0_div1", "dummy_name"};
+static const char *gem1_mux_parents[] __initconst = {"gem1_div1", "dummy_name"};
+static const char *can0_mio_mux2_parents[] __initconst = {"can0_gate",
"can0_mio_mux"};
-static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate",
+static const char *can1_mio_mux2_parents[] __initconst = {"can1_gate",
"can1_mio_mux"};
-static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div",
- dummy_nm};
+static const char *dbg_emio_mux_parents[] __initconst = {"dbg_div",
+ "dummy_name"};
-static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"};
-static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"};
-static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"};
-static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"};
+static const char *dbgtrc_emio_input_names[] __initconst = {"trace_emio_clk"};
+static const char *gem0_emio_input_names[] __initconst = {"gem0_emio_clk"};
+static const char *gem1_emio_input_names[] __initconst = {"gem1_emio_clk"};
+static const char *swdt_ext_clk_input_names[] __initconst = {"swdt_ext_clk"};
static void __init zynq_clk_register_fclk(enum zynq_clk fclk,
const char *clk_name, void __iomem *fclk_ctrl_reg,
@@ -230,6 +228,7 @@ static void __init zynq_clk_setup(struct device_node *np)
const char *periph_parents[4];
const char *swdt_ext_clk_mux_parents[2];
const char *can_mio_mux_parents[NUM_MIO_PINS];
+ const char *dummy_nm = "dummy_name";
pr_info("Zynq clock init\n");
@@ -619,5 +618,4 @@ void __init zynq_clock_init(void)
np_err:
of_node_put(np);
BUG();
- return;
}
diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c
index cec97596fe65..00d72fb5c036 100644
--- a/drivers/clk/zynq/pll.c
+++ b/drivers/clk/zynq/pll.c
@@ -211,10 +211,8 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent,
};
pll = kmalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll) {
- pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__);
+ if (!pll)
return ERR_PTR(-ENOMEM);
- }
/* Populate the struct */
pll->hw.init = &initd;
OpenPOWER on IntegriCloud