summaryrefslogtreecommitdiffstats
path: root/drivers/clk/shmobile
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-02-21 12:30:30 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-21 12:30:30 -0800
commit18a8d49973667aa016e68826eeb374788b7c63b0 (patch)
treed7f90fcf05087ce50ee931b596e60e4c3c082a29 /drivers/clk/shmobile
parentc189cb8ef62832f33b6cf757350a0270532a1ad8 (diff)
parentec02ace8ca0a50eef430d3676de5c5fa978b0e29 (diff)
downloadop-kernel-dev-18a8d49973667aa016e68826eeb374788b7c63b0.zip
op-kernel-dev-18a8d49973667aa016e68826eeb374788b7c63b0.tar.gz
Merge tag 'clk-for-linus-3.20' of git://git.linaro.org/people/mike.turquette/linux
Pull clock framework updates from Mike Turquette: "The clock framework changes contain the usual driver additions, enhancements and fixes mostly for ARM32, ARM64, MIPS and Power-based devices. Additionally the framework core underwent a bit of surgery with two major changes: - The boundary between the clock core and clock providers (e.g clock drivers) is now more well defined with dedicated provider helper functions. struct clk no longer maps 1:1 with the hardware clock but is a true per-user cookie which helps us tracker users of hardware clocks and debug bad behavior. - The addition of rate constraints for clocks. Rate ranges are now supported which are analogous to the voltage ranges in the regulator framework. Unfortunately these changes to the core created some breakeage. We think we fixed it all up but for this reason there are lots of last minute commits trying to undo the damage" * tag 'clk-for-linus-3.20' of git://git.linaro.org/people/mike.turquette/linux: (113 commits) clk: Only recalculate the rate if needed Revert "clk: mxs: Fix invalid 32-bit access to frac registers" clk: qoriq: Add support for the platform PLL powerpc/corenet: Enable CLK_QORIQ clk: Replace explicit clk assignment with __clk_hw_set_clk clk: Add __clk_hw_set_clk helper function clk: Don't dereference parent clock if is NULL MIPS: Alchemy: Remove bogus args from alchemy_clk_fgcs_detr clkdev: Always allocate a struct clk and call __clk_get() w/ CCF clk: shmobile: div6: Avoid division by zero in .round_rate() clk: mxs: Fix invalid 32-bit access to frac registers clk: omap: compile legacy omap3 clocks conditionally clkdev: Export clk_register_clkdev clk: Add rate constraints to clocks clk: remove clk-private.h pci: xgene: do not use clk-private.h arm: omap2+ remove dead clock code clk: Make clk API return per-user struct clk instances clk: tegra: Define PLLD_DSI and remove dsia(b)_mux clk: tegra: Add support for the Tegra132 CAR IP block ...
Diffstat (limited to 'drivers/clk/shmobile')
-rw-r--r--drivers/clk/shmobile/Makefile2
-rw-r--r--drivers/clk/shmobile/clk-div6.c18
-rw-r--r--drivers/clk/shmobile/clk-r8a73a4.c241
-rw-r--r--drivers/clk/shmobile/clk-rcar-gen2.c88
4 files changed, 345 insertions, 4 deletions
diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile
index f83980f..0689d7f 100644
--- a/drivers/clk/shmobile/Makefile
+++ b/drivers/clk/shmobile/Makefile
@@ -1,9 +1,11 @@
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
+obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o
obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o
obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
+obj-$(CONFIG_ARCH_R8A7793) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_SH73A0) += clk-sh73a0.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c
index 639241e..036a692 100644
--- a/drivers/clk/shmobile/clk-div6.c
+++ b/drivers/clk/shmobile/clk-div6.c
@@ -54,12 +54,19 @@ static int cpg_div6_clock_enable(struct clk_hw *hw)
static void cpg_div6_clock_disable(struct clk_hw *hw)
{
struct div6_clock *clock = to_div6_clock(hw);
+ u32 val;
- /* DIV6 clocks require the divisor field to be non-zero when stopping
- * the clock.
+ val = clk_readl(clock->reg);
+ val |= CPG_DIV6_CKSTP;
+ /*
+ * DIV6 clocks require the divisor field to be non-zero when stopping
+ * the clock. However, some clocks (e.g. ZB on sh73a0) fail to be
+ * re-enabled later if the divisor field is changed when stopping the
+ * clock
*/
- clk_writel(clk_readl(clock->reg) | CPG_DIV6_CKSTP | CPG_DIV6_DIV_MASK,
- clock->reg);
+ if (!(val & CPG_DIV6_DIV_MASK))
+ val |= CPG_DIV6_DIV_MASK;
+ clk_writel(val, clock->reg);
}
static int cpg_div6_clock_is_enabled(struct clk_hw *hw)
@@ -83,6 +90,9 @@ static unsigned int cpg_div6_clock_calc_div(unsigned long rate,
{
unsigned int div;
+ if (!rate)
+ rate = 1;
+
div = DIV_ROUND_CLOSEST(parent_rate, rate);
return clamp_t(unsigned int, div, 1, 64);
}
diff --git a/drivers/clk/shmobile/clk-r8a73a4.c b/drivers/clk/shmobile/clk-r8a73a4.c
new file mode 100644
index 0000000..29b9a0b0
--- /dev/null
+++ b/drivers/clk/shmobile/clk-r8a73a4.c
@@ -0,0 +1,241 @@
+/*
+ * r8a73a4 Core CPG Clocks
+ *
+ * Copyright (C) 2014 Ulrich Hecht
+ *
+ * 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 <linux/clkdev.h>
+#include <linux/clk/shmobile.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/spinlock.h>
+
+struct r8a73a4_cpg {
+ struct clk_onecell_data data;
+ spinlock_t lock;
+ void __iomem *reg;
+};
+
+#define CPG_CKSCR 0xc0
+#define CPG_FRQCRA 0x00
+#define CPG_FRQCRB 0x04
+#define CPG_FRQCRC 0xe0
+#define CPG_PLL0CR 0xd8
+#define CPG_PLL1CR 0x28
+#define CPG_PLL2CR 0x2c
+#define CPG_PLL2HCR 0xe4
+#define CPG_PLL2SCR 0xf4
+
+#define CLK_ENABLE_ON_INIT BIT(0)
+
+struct div4_clk {
+ const char *name;
+ unsigned int reg;
+ unsigned int shift;
+};
+
+static struct div4_clk div4_clks[] = {
+ { "i", CPG_FRQCRA, 20 },
+ { "m3", CPG_FRQCRA, 12 },
+ { "b", CPG_FRQCRA, 8 },
+ { "m1", CPG_FRQCRA, 4 },
+ { "m2", CPG_FRQCRA, 0 },
+ { "zx", CPG_FRQCRB, 12 },
+ { "zs", CPG_FRQCRB, 8 },
+ { "hp", CPG_FRQCRB, 4 },
+ { NULL, 0, 0 },
+};
+
+static const struct clk_div_table div4_div_table[] = {
+ { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 },
+ { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 },
+ { 12, 10 }, { 0, 0 }
+};
+
+static struct clk * __init
+r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg,
+ const char *name)
+{
+ const struct clk_div_table *table = NULL;
+ const char *parent_name;
+ unsigned int shift, reg;
+ unsigned int mult = 1;
+ unsigned int div = 1;
+
+
+ if (!strcmp(name, "main")) {
+ u32 ckscr = clk_readl(cpg->reg + CPG_CKSCR);
+
+ switch ((ckscr >> 28) & 3) {
+ case 0: /* extal1 */
+ parent_name = of_clk_get_parent_name(np, 0);
+ break;
+ case 1: /* extal1 / 2 */
+ parent_name = of_clk_get_parent_name(np, 0);
+ div = 2;
+ break;
+ case 2: /* extal2 */
+ parent_name = of_clk_get_parent_name(np, 1);
+ break;
+ case 3: /* extal2 / 2 */
+ parent_name = of_clk_get_parent_name(np, 1);
+ div = 2;
+ break;
+ }
+ } else if (!strcmp(name, "pll0")) {
+ /* PLL0/1 are configurable multiplier clocks. Register them as
+ * fixed factor clocks for now as there's no generic multiplier
+ * clock implementation and we currently have no need to change
+ * the multiplier value.
+ */
+ u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
+
+ parent_name = "main";
+ mult = ((value >> 24) & 0x7f) + 1;
+ if (value & BIT(20))
+ div = 2;
+ } else if (!strcmp(name, "pll1")) {
+ u32 value = clk_readl(cpg->reg + CPG_PLL1CR);
+
+ parent_name = "main";
+ /* XXX: enable bit? */
+ mult = ((value >> 24) & 0x7f) + 1;
+ if (value & BIT(7))
+ div = 2;
+ } else if (!strncmp(name, "pll2", 4)) {
+ u32 value, cr;
+
+ switch (name[4]) {
+ case 0:
+ cr = CPG_PLL2CR;
+ break;
+ case 's':
+ cr = CPG_PLL2SCR;
+ break;
+ case 'h':
+ cr = CPG_PLL2HCR;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+ value = clk_readl(cpg->reg + cr);
+ switch ((value >> 5) & 7) {
+ case 0:
+ parent_name = "main";
+ div = 2;
+ break;
+ case 1:
+ parent_name = "extal2";
+ div = 2;
+ break;
+ case 3:
+ parent_name = "extal2";
+ div = 4;
+ break;
+ case 4:
+ parent_name = "main";
+ break;
+ case 5:
+ parent_name = "extal2";
+ break;
+ default:
+ pr_warn("%s: unexpected parent of %s\n", __func__,
+ name);
+ return ERR_PTR(-EINVAL);
+ }
+ /* XXX: enable bit? */
+ mult = ((value >> 24) & 0x7f) + 1;
+ } else if (!strcmp(name, "z") || !strcmp(name, "z2")) {
+ u32 shift = 8;
+
+ parent_name = "pll0";
+ if (name[1] == '2') {
+ div = 2;
+ shift = 0;
+ }
+ div *= 32;
+ mult = 0x20 - ((clk_readl(cpg->reg + CPG_FRQCRC) >> shift)
+ & 0x1f);
+ } else {
+ struct div4_clk *c;
+
+ for (c = div4_clks; c->name; c++) {
+ if (!strcmp(name, c->name))
+ break;
+ }
+ if (!c->name)
+ return ERR_PTR(-EINVAL);
+
+ parent_name = "pll1";
+ table = div4_div_table;
+ reg = c->reg;
+ shift = c->shift;
+ }
+
+ if (!table) {
+ return clk_register_fixed_factor(NULL, name, parent_name, 0,
+ mult, div);
+ } else {
+ return clk_register_divider_table(NULL, name, parent_name, 0,
+ cpg->reg + reg, shift, 4, 0,
+ table, &cpg->lock);
+ }
+}
+
+static void __init r8a73a4_cpg_clocks_init(struct device_node *np)
+{
+ struct r8a73a4_cpg *cpg;
+ struct clk **clks;
+ unsigned int i;
+ int num_clks;
+
+ num_clks = of_property_count_strings(np, "clock-output-names");
+ if (num_clks < 0) {
+ pr_err("%s: failed to count clocks\n", __func__);
+ return;
+ }
+
+ cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
+ clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL);
+ if (cpg == NULL || clks == NULL) {
+ /* We're leaking memory on purpose, there's no point in cleaning
+ * up as the system won't boot anyway.
+ */
+ return;
+ }
+
+ spin_lock_init(&cpg->lock);
+
+ cpg->data.clks = clks;
+ cpg->data.clk_num = num_clks;
+
+ cpg->reg = of_iomap(np, 0);
+ if (WARN_ON(cpg->reg == NULL))
+ return;
+
+ for (i = 0; i < num_clks; ++i) {
+ const char *name;
+ struct clk *clk;
+
+ of_property_read_string_index(np, "clock-output-names", i,
+ &name);
+
+ clk = r8a73a4_cpg_register_clock(np, cpg, name);
+ if (IS_ERR(clk))
+ pr_err("%s: failed to register %s %s clock (%ld)\n",
+ __func__, np->name, name, PTR_ERR(clk));
+ else
+ cpg->data.clks[i] = clk;
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
+}
+CLK_OF_DECLARE(r8a73a4_cpg_clks, "renesas,r8a73a4-cpg-clocks",
+ r8a73a4_cpg_clocks_init);
diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c
index e996425..acfb6d7 100644
--- a/drivers/clk/shmobile/clk-rcar-gen2.c
+++ b/drivers/clk/shmobile/clk-rcar-gen2.c
@@ -33,6 +33,8 @@ struct rcar_gen2_cpg {
#define CPG_FRQCRC 0x000000e0
#define CPG_FRQCRC_ZFC_MASK (0x1f << 8)
#define CPG_FRQCRC_ZFC_SHIFT 8
+#define CPG_ADSPCKCR 0x0000025c
+#define CPG_RCANCKCR 0x00000270
/* -----------------------------------------------------------------------------
* Z Clock
@@ -161,6 +163,88 @@ static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg)
return clk;
}
+static struct clk * __init cpg_rcan_clk_register(struct rcar_gen2_cpg *cpg,
+ struct device_node *np)
+{
+ const char *parent_name = of_clk_get_parent_name(np, 1);
+ struct clk_fixed_factor *fixed;
+ struct clk_gate *gate;
+ struct clk *clk;
+
+ fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return ERR_PTR(-ENOMEM);
+
+ fixed->mult = 1;
+ fixed->div = 6;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(fixed);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ gate->reg = cpg->reg + CPG_RCANCKCR;
+ gate->bit_idx = 8;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->lock = &cpg->lock;
+
+ clk = clk_register_composite(NULL, "rcan", &parent_name, 1, NULL, NULL,
+ &fixed->hw, &clk_fixed_factor_ops,
+ &gate->hw, &clk_gate_ops, 0);
+ if (IS_ERR(clk)) {
+ kfree(gate);
+ kfree(fixed);
+ }
+
+ return clk;
+}
+
+/* ADSP divisors */
+static const struct clk_div_table cpg_adsp_div_table[] = {
+ { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 },
+ { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 },
+ { 10, 36 }, { 11, 48 }, { 0, 0 },
+};
+
+static struct clk * __init cpg_adsp_clk_register(struct rcar_gen2_cpg *cpg)
+{
+ const char *parent_name = "pll1";
+ struct clk_divider *div;
+ struct clk_gate *gate;
+ struct clk *clk;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->reg = cpg->reg + CPG_ADSPCKCR;
+ div->width = 4;
+ div->table = cpg_adsp_div_table;
+ div->lock = &cpg->lock;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(div);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ gate->reg = cpg->reg + CPG_ADSPCKCR;
+ gate->bit_idx = 8;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->lock = &cpg->lock;
+
+ clk = clk_register_composite(NULL, "adsp", &parent_name, 1, NULL, NULL,
+ &div->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops, 0);
+ if (IS_ERR(clk)) {
+ kfree(gate);
+ kfree(div);
+ }
+
+ return clk;
+}
+
/* -----------------------------------------------------------------------------
* CPG Clock Data
*/
@@ -263,6 +347,10 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
shift = 0;
} else if (!strcmp(name, "z")) {
return cpg_z_clk_register(cpg);
+ } else if (!strcmp(name, "rcan")) {
+ return cpg_rcan_clk_register(cpg, np);
+ } else if (!strcmp(name, "adsp")) {
+ return cpg_adsp_clk_register(cpg);
} else {
return ERR_PTR(-EINVAL);
}
OpenPOWER on IntegriCloud