new file mode 100644
@@ -0,0 +1,47 @@
+Binding for Marvell MMP series master clock.
+
+The MMP related device tree support for clock based on the clock type not clock
+controller. So specific SOC, user need define the DT file for the clock such as
+pxa910-clock.dtsi.
+
+Almost all types of clock will need parameter as "register", and it will map the
+"register" before access it. If every clock map the "register" seperately, there
+will be a lot of waste.
+
+A master clock is defined for this kind of situation. It will be responsible for
+map the registers for all clocks that lists as its children in DT file.
+
+Required properties
+- compatible : It should be "marvell,mmp-clk-master".
+
+
+Optional properties:
+- reg : The register start and range the master clock covered.
+
+Optional properties for child node:
+- marvell,reg-offset : It is a two-values item - <register_index regiser_offset>.
+ Master node will map the registers for all its children. So
+ for the child it need to pass the information about register
+ index and offset. "register_index" indicates which register space
+ it from because master clock can have mutiple register space in
+ "reg". "register_offset" indicates the offset in the register
+ space.
+
+Examples
+There are two clocks, clk1 has register at 0xd4210010, and clk2 has register at
+0xd42100c0.
+
+apmu_clk {
+ compatible = "marvell,mmp-clk-master";
+ reg = <0xd4210000 0x1000>;
+
+ clk1 {
+ ...
+ marvell,reg-offset = <0 0x10>;
+ };
+
+ clk2 {
+ ...
+ marvell,reg-offset = <0 0xc0>;
+ };
+};
@@ -5,6 +5,10 @@
obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o \
clk-mix-composite.o
+ifneq ($(CONFIG_OF),)
+obj-y += clk-master-node.o
+endif
+
obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o
new file mode 100644
@@ -0,0 +1,195 @@
+/*
+ * mmp master clock source file
+ *
+ * Copyright (C) 2014 Marvell
+ * Chao Xie <chao.xie@marvell.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/ioport.h>
+
+#include "clk.h"
+
+#define MAX_REG 8
+
+struct mmp_clk_master_node {
+ unsigned int reg_base[MAX_REG];
+ void __iomem *reg[MAX_REG];
+ struct device_node *np;
+ struct list_head node;
+};
+
+static LIST_HEAD(master_list);
+static DEFINE_MUTEX(master_mutex);
+
+static void mmp_clk_master_setup(struct device_node *np)
+{
+ struct mmp_clk_master_node *node;
+ struct resource res;
+ int i, ret;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ pr_err("%s:%s failed to allocate master node.\n",
+ __func__, np->name);
+ return;
+ }
+
+ for (i = 0; i < MAX_REG; i++) {
+ ret = of_address_to_resource(np, i, &res);
+ if (ret)
+ break;
+ node->reg_base[i] = res.start;
+ node->reg[i] = ioremap(res.start, resource_size(&res));
+ if (!node->reg[i]) {
+ pr_err("%s:%s failed to map register.\n",
+ __func__, np->name);
+ goto error;
+ }
+ }
+
+ node->np = np;
+ INIT_LIST_HEAD(&node->node);
+
+ mutex_lock(&master_mutex);
+
+ list_add(&node->node, &master_list);
+
+ mutex_unlock(&master_mutex);
+
+ return;
+error:
+ for (i--; i >= 0; i--)
+ iounmap(node->reg[i]);
+
+ kfree(node);
+}
+
+struct of_device_id mmp_clk_master_of_id[] = {
+ {
+ .compatible = "marvell,mmp-clk-master",
+ .data = mmp_clk_master_setup,
+ },
+ { },
+};
+
+static struct mmp_clk_master_node *get_master_node(struct device_node *child)
+{
+ struct device_node *master;
+ struct mmp_clk_master_node *node;
+
+ /* Find the master device node */
+ master = child;
+ do {
+ master = of_get_next_parent(master);
+ } while (!of_match_node(mmp_clk_master_of_id, master));
+
+ mutex_lock(&master_mutex);
+
+ list_for_each_entry(node, &master_list, node) {
+ if (node->np == master) {
+ mutex_unlock(&master_mutex);
+ return node;
+ }
+ }
+
+ mutex_unlock(&master_mutex);
+
+ return NULL;
+}
+
+static void __iomem *get_child_reg(struct device_node *child,
+ unsigned int reg_index,
+ unsigned int *reg_base)
+{
+ struct mmp_clk_master_node *node;
+
+ if (reg_index >= MAX_REG) {
+ pr_err("%s:%s reg_index too big.\n", __func__, child->name);
+ return NULL;
+ }
+
+ node = get_master_node(child);
+ if (!node) {
+ pr_err("%s:%s failed to get master node\n",
+ __func__, child->name);
+ return NULL;
+ }
+
+ *reg_base = node->reg_base[reg_index];
+
+ return node->reg[reg_index];
+}
+
+void __iomem *of_mmp_clk_get_reg(struct device_node *np,
+ unsigned int index,
+ unsigned int *reg_phys)
+{
+ const __be32 *prop;
+ unsigned int proplen, size;
+ u32 reg_index, reg_offset;
+ unsigned int reg_base;
+ void __iomem *reg;
+
+ prop = of_get_property(np, "marvell,reg-offset", &proplen);
+ if (!prop) {
+ pr_err("%s:%s can not find marvell,reg-offset\n",
+ __func__, np->name);
+ return NULL;
+ }
+
+ size = proplen / sizeof(u32);
+
+ if ((proplen % sizeof(u32)) || (size <= (index * 2))) {
+ pr_err("%s:%s prop len is not correct\n",
+ __func__, np->name);
+ return NULL;
+ }
+
+ reg_index = be32_to_cpup(prop + index * 2);
+ reg_offset = be32_to_cpup(prop + index * 2 + 1);
+ reg = get_child_reg(np, reg_index, ®_base);
+ if (!reg) {
+ pr_err("%s:%s failed to get reg\n",
+ __func__, np->name);
+ return NULL;
+ }
+
+ *reg_phys = reg_base + reg_offset;
+
+ return reg + reg_offset;
+}
+
+struct device_node *of_mmp_clk_master_init(struct device_node *from)
+{
+ struct device_node *parent, *child;
+ const struct of_device_id *match;
+ of_clk_init_cb_t clk_init_cb;
+
+ parent = from;
+ parent = of_find_matching_node_and_match(from, mmp_clk_master_of_id,
+ &match);
+ if (parent) {
+ clk_init_cb = (of_clk_init_cb_t)match->data;
+ clk_init_cb(parent);
+ for_each_child_of_node(parent, child) {
+ match = of_match_node(&__clk_of_table, child);
+ if (!match)
+ continue;
+ clk_init_cb = (of_clk_init_cb_t)match->data;
+ clk_init_cb(child);
+ }
+ }
+
+ return parent;
+}
@@ -140,6 +140,13 @@ extern struct clk *mmp_clk_register_composite(struct device *dev,
unsigned long flags);
+/* Master clock exported APIs and data. */
+extern void __iomem *of_mmp_clk_get_reg(struct device_node *np,
+ unsigned int reg_index,
+ unsigned int *reg_phys);
+struct device_node *of_mmp_clk_master_init(struct device_node *from);
+
+
extern struct clk *mmp_clk_register_pll2(const char *name,
const char *parent_name, unsigned long flags);
extern struct clk *mmp_clk_register_apbc(const char *name,