summaryrefslogtreecommitdiffstats
path: root/drivers/clk/versatile
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-01-20 18:03:56 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-20 18:03:56 -0800
commit6b5a12dbca7a8681ecb78dbebaedc1f8364ebd10 (patch)
tree497bc67a98e8bf5247dd453d05ab0dba4c31e424 /drivers/clk/versatile
parent5083c54264d21bf9b8a4766068f51581854d772c (diff)
parent2cb58c016673c4c8b8ec59e71c5ec4b2c6a76afb (diff)
downloadlinux-6b5a12dbca7a8681ecb78dbebaedc1f8364ebd10.tar.bz2
Merge tag 'armsoc-multiplatform' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC multiplatform code updates from Arnd Bergmann: "This branch is the culmination of 5 years of effort to bring the ARMv6 and ARMv7 platforms together such that they can all be enabled and boot the same kernel. It has been a tremendous amount of cleanup and refactoring by a huge number of people, and creation of several new (and major) subsystems to better abstract out all the platform details in an appropriate manner. The bulk of this branch is a large patchset from Arnd that brings several of the more minor and older platforms we have closer to multiplatform support. Among these are MMP, S3C64xx, Orion5x, mv78xx0 and realview Much of this is moving around header files from old mach directories, but there are also some cleanup patches of debug_ll (lowlevel debug per-platform options) and other parts. Linus Walleij also has some patchs to clean up the older ARM Realview platforms by finally introducing DT support, and Rob Herring has some for ARM Versatile which is now DT-only. Both of these platforms are now multiplatform. Finally, a couple of patches from Russell for Dove PMU, and a fix from Valentin Rothberg for Exynos ADC, which were rebased on top of the series to avoid conflicts" * tag 'armsoc-multiplatform' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (75 commits) ARM: realview: don't select SMP_ON_UP for UP builds ARM: s3c: simplify s3c_irqwake_{e,}intallow definition ARM: s3c64xx: fix pm-debug compilation iio: exynos-adc: fix irqf_oneshot.cocci warnings ARM: realview: build realview-dt SMP support only when used ARM: realview: select apropriate targets ARM: realview: clean up header files ARM: realview: make all header files local ARM: no longer make CPU targets visible separately ARM: integrator: use explicit core module options ARM: realview: enable multiplatform ARM: make default platform work for NOMMU ARM: debug-ll: move DEBUG_LL_UART_EFM32 to correct Kconfig location ARM: defconfig: use correct debug_ll settings ARM: versatile: convert to multi-platform ARM: versatile: merge mach code into a single file ARM: versatile: switch to DT only booting and remove legacy code ARM: versatile: add DT based PCI detection ARM: pxa: mark ezx structures as __maybe_unused ARM: pxa: mark raumfeld init functions as __maybe_unused ...
Diffstat (limited to 'drivers/clk/versatile')
-rw-r--r--drivers/clk/versatile/Kconfig5
-rw-r--r--drivers/clk/versatile/clk-icst.c194
-rw-r--r--drivers/clk/versatile/clk-realview.c10
3 files changed, 172 insertions, 37 deletions
diff --git a/drivers/clk/versatile/Kconfig b/drivers/clk/versatile/Kconfig
index fc50b6264bed..a6da2aa09f83 100644
--- a/drivers/clk/versatile/Kconfig
+++ b/drivers/clk/versatile/Kconfig
@@ -1,6 +1,9 @@
config COMMON_CLK_VERSATILE
bool "Clock driver for ARM Reference designs"
- depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 || COMPILE_TEST
+ depends on ARCH_INTEGRATOR || ARCH_REALVIEW || \
+ ARCH_VERSATILE || ARCH_VEXPRESS || ARM64 || \
+ COMPILE_TEST
+ select REGMAP_MMIO
---help---
Supports clocking on ARM Reference designs:
- Integrator/AP and Integrator/CP
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
index 08c5ee976879..e62f8cb2c9b5 100644
--- a/drivers/clk/versatile/clk-icst.c
+++ b/drivers/clk/versatile/clk-icst.c
@@ -3,7 +3,7 @@
* We wrap the custom interface from <asm/hardware/icst.h> into the generic
* clock framework.
*
- * Copyright (C) 2012 Linus Walleij
+ * Copyright (C) 2012-2015 Linus Walleij
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -19,9 +19,14 @@
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
#include "clk-icst.h"
+/* Magic unlocking token used on all Versatile boards */
+#define VERSATILE_LOCK_VAL 0xA05F
+
/**
* struct clk_icst - ICST VCO clock wrapper
* @hw: corresponding clock hardware entry
@@ -32,8 +37,9 @@
*/
struct clk_icst {
struct clk_hw hw;
- void __iomem *vcoreg;
- void __iomem *lockreg;
+ struct regmap *map;
+ u32 vcoreg_off;
+ u32 lockreg_off;
struct icst_params *params;
unsigned long rate;
};
@@ -41,53 +47,67 @@ struct clk_icst {
#define to_icst(_hw) container_of(_hw, struct clk_icst, hw)
/**
- * vco_get() - get ICST VCO settings from a certain register
- * @vcoreg: register containing the VCO settings
+ * vco_get() - get ICST VCO settings from a certain ICST
+ * @icst: the ICST clock to get
+ * @vco: the VCO struct to return the value in
*/
-static struct icst_vco vco_get(void __iomem *vcoreg)
+static int vco_get(struct clk_icst *icst, struct icst_vco *vco)
{
u32 val;
- struct icst_vco vco;
+ int ret;
- val = readl(vcoreg);
- vco.v = val & 0x1ff;
- vco.r = (val >> 9) & 0x7f;
- vco.s = (val >> 16) & 03;
- return vco;
+ ret = regmap_read(icst->map, icst->vcoreg_off, &val);
+ if (ret)
+ return ret;
+ vco->v = val & 0x1ff;
+ vco->r = (val >> 9) & 0x7f;
+ vco->s = (val >> 16) & 03;
+ return 0;
}
/**
* vco_set() - commit changes to an ICST VCO
- * @locreg: register to poke to unlock the VCO for writing
- * @vcoreg: register containing the VCO settings
- * @vco: ICST VCO parameters to commit
+ * @icst: the ICST clock to set
+ * @vco: the VCO struct to set the changes from
*/
-static void vco_set(void __iomem *lockreg,
- void __iomem *vcoreg,
- struct icst_vco vco)
+static int vco_set(struct clk_icst *icst, struct icst_vco vco)
{
u32 val;
+ int ret;
- val = readl(vcoreg) & ~0x7ffff;
+ ret = regmap_read(icst->map, icst->vcoreg_off, &val);
+ if (ret)
+ return ret;
val |= vco.v | (vco.r << 9) | (vco.s << 16);
/* This magic unlocks the VCO so it can be controlled */
- writel(0xa05f, lockreg);
- writel(val, vcoreg);
+ ret = regmap_write(icst->map, icst->lockreg_off, VERSATILE_LOCK_VAL);
+ if (ret)
+ return ret;
+ ret = regmap_write(icst->map, icst->vcoreg_off, val);
+ if (ret)
+ return ret;
/* This locks the VCO again */
- writel(0, lockreg);
+ ret = regmap_write(icst->map, icst->lockreg_off, 0);
+ if (ret)
+ return ret;
+ return 0;
}
-
static unsigned long icst_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ int ret;
if (parent_rate)
icst->params->ref = parent_rate;
- vco = vco_get(icst->vcoreg);
+ ret = vco_get(icst, &vco);
+ if (ret) {
+ pr_err("ICST: could not get VCO setting\n");
+ return 0;
+ }
icst->rate = icst_hz(icst->params, vco);
return icst->rate;
}
@@ -112,8 +132,7 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
icst->params->ref = parent_rate;
vco = icst_hz_to_vco(icst->params, rate);
icst->rate = icst_hz(icst->params, vco);
- vco_set(icst->lockreg, icst->vcoreg, vco);
- return 0;
+ return vco_set(icst, vco);
}
static const struct clk_ops icst_ops = {
@@ -122,11 +141,11 @@ static const struct clk_ops icst_ops = {
.set_rate = icst_set_rate,
};
-struct clk *icst_clk_register(struct device *dev,
- const struct clk_icst_desc *desc,
- const char *name,
- const char *parent_name,
- void __iomem *base)
+static struct clk *icst_clk_setup(struct device *dev,
+ const struct clk_icst_desc *desc,
+ const char *name,
+ const char *parent_name,
+ struct regmap *map)
{
struct clk *clk;
struct clk_icst *icst;
@@ -151,10 +170,11 @@ struct clk *icst_clk_register(struct device *dev,
init.flags = CLK_IS_ROOT;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
+ icst->map = map;
icst->hw.init = &init;
icst->params = pclone;
- icst->vcoreg = base + desc->vco_offset;
- icst->lockreg = base + desc->lock_offset;
+ icst->vcoreg_off = desc->vco_offset;
+ icst->lockreg_off = desc->lock_offset;
clk = clk_register(dev, &icst->hw);
if (IS_ERR(clk)) {
@@ -164,4 +184,112 @@ struct clk *icst_clk_register(struct device *dev,
return clk;
}
+
+struct clk *icst_clk_register(struct device *dev,
+ const struct clk_icst_desc *desc,
+ const char *name,
+ const char *parent_name,
+ void __iomem *base)
+{
+ struct regmap_config icst_regmap_conf = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+ struct regmap *map;
+
+ map = regmap_init_mmio(dev, base, &icst_regmap_conf);
+ if (IS_ERR(map)) {
+ pr_err("could not initialize ICST regmap\n");
+ return ERR_CAST(map);
+ }
+ return icst_clk_setup(dev, desc, name, parent_name, map);
+}
EXPORT_SYMBOL_GPL(icst_clk_register);
+
+#ifdef CONFIG_OF
+/*
+ * In a device tree, an memory-mapped ICST clock appear as a child
+ * of a syscon node. Assume this and probe it only as a child of a
+ * syscon.
+ */
+
+static const struct icst_params icst525_params = {
+ .vco_max = ICST525_VCO_MAX_5V,
+ .vco_min = ICST525_VCO_MIN,
+ .vd_min = 8,
+ .vd_max = 263,
+ .rd_min = 3,
+ .rd_max = 65,
+ .s2div = icst525_s2div,
+ .idx2s = icst525_idx2s,
+};
+
+static const struct icst_params icst307_params = {
+ .vco_max = ICST307_VCO_MAX,
+ .vco_min = ICST307_VCO_MIN,
+ .vd_min = 4 + 8,
+ .vd_max = 511 + 8,
+ .rd_min = 1 + 2,
+ .rd_max = 127 + 2,
+ .s2div = icst307_s2div,
+ .idx2s = icst307_idx2s,
+};
+
+static void __init of_syscon_icst_setup(struct device_node *np)
+{
+ struct device_node *parent;
+ struct regmap *map;
+ struct clk_icst_desc icst_desc;
+ const char *name = np->name;
+ const char *parent_name;
+ struct clk *regclk;
+
+ /* We do not release this reference, we are using it perpetually */
+ parent = of_get_parent(np);
+ if (!parent) {
+ pr_err("no parent node for syscon ICST clock\n");
+ return;
+ }
+ map = syscon_node_to_regmap(parent);
+ if (IS_ERR(map)) {
+ pr_err("no regmap for syscon ICST clock parent\n");
+ return;
+ }
+
+ if (of_property_read_u32(np, "vco-offset", &icst_desc.vco_offset)) {
+ pr_err("no VCO register offset for ICST clock\n");
+ return;
+ }
+ if (of_property_read_u32(np, "lock-offset", &icst_desc.lock_offset)) {
+ pr_err("no lock register offset for ICST clock\n");
+ return;
+ }
+
+ if (of_device_is_compatible(np, "arm,syscon-icst525"))
+ icst_desc.params = &icst525_params;
+ else if (of_device_is_compatible(np, "arm,syscon-icst307"))
+ icst_desc.params = &icst307_params;
+ else {
+ pr_err("unknown ICST clock %s\n", name);
+ return;
+ }
+
+ /* Parent clock name is not the same as node parent */
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map);
+ if (IS_ERR(regclk)) {
+ pr_err("error setting up syscon ICST clock %s\n", name);
+ return;
+ }
+ of_clk_add_provider(np, of_clk_src_simple_get, regclk);
+ pr_debug("registered syscon ICST clock %s\n", name);
+}
+
+CLK_OF_DECLARE(arm_syscon_icst525_clk,
+ "arm,syscon-icst525", of_syscon_icst_setup);
+CLK_OF_DECLARE(arm_syscon_icst307_clk,
+ "arm,syscon-icst307", of_syscon_icst_setup);
+
+#endif
diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c
index 86f70997d59d..bd4dd2463e23 100644
--- a/drivers/clk/versatile/clk-realview.c
+++ b/drivers/clk/versatile/clk-realview.c
@@ -11,11 +11,15 @@
#include <linux/io.h>
#include <linux/clk-provider.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
-
#include "clk-icst.h"
+#define REALVIEW_SYS_OSC0_OFFSET 0x0C
+#define REALVIEW_SYS_OSC1_OFFSET 0x10
+#define REALVIEW_SYS_OSC2_OFFSET 0x14
+#define REALVIEW_SYS_OSC3_OFFSET 0x18
+#define REALVIEW_SYS_OSC4_OFFSET 0x1C /* OSC1 for RealView/AB */
+#define REALVIEW_SYS_LOCK_OFFSET 0x20
+
/*
* Implementation of the ARM RealView clock trees.
*/