diff mbox

[1/2] ARM: mach-shmobile: clock-sh7372: Add FSIDIV clock support

Message ID 20101013142111.GA13310@linux-sh.org (mailing list archive)
State RFC
Headers show

Commit Message

Paul Mundt Oct. 13, 2010, 2:21 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/sh/clk.c b/drivers/sh/clk.c
index 661a801..a20d309 100644
--- a/drivers/sh/clk.c
+++ b/drivers/sh/clk.c
@@ -25,6 +25,7 @@ 
 #include <linux/sysdev.h>
 #include <linux/seq_file.h>
 #include <linux/err.h>
+#include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/debugfs.h>
 #include <linux/cpufreq.h>
@@ -233,6 +234,14 @@  EXPORT_SYMBOL_GPL(clk_enable);
 
 static LIST_HEAD(root_clks);
 
+static struct clk *lookup_root_clock(struct clk *clk)
+{
+	while (clk->parent)
+		clk = clk->parent;
+
+	return clk;
+}
+
 /**
  * recalculate_root_clocks - recalculate and propagate all root clocks
  *
@@ -251,8 +260,76 @@  void recalculate_root_clocks(void)
 	}
 }
 
+static struct clk_mapping dummy_mapping;
+
+static int clk_establish_mapping(struct clk *clk)
+{
+	struct clk_mapping *mapping = clk->mapping;
+
+	/*
+	 * Propagate mappings.
+	 */
+	if (!mapping) {
+		struct clk *clkp;
+
+		/*
+		 * dummy mapping for root clocks with no specified ranges
+		 */
+		if (!clk->parent) {
+			clk->mapping = &dummy_mapping;
+			return 0;
+		}
+
+		/*
+		 * If we're on a child clock and it provides no mapping of its
+		 * own, inherit the mapping from its root clock.
+		 */
+		clkp = lookup_root_clock(clk);
+		mapping = clkp->mapping;
+		BUG_ON(!mapping);
+	}
+
+	/*
+	 * Establish initial mapping.
+	 */
+	if (!mapping->base && mapping->phys) {
+		kref_init(&mapping->ref);
+		mapping->base = ioremap_nocache(mapping->phys, mapping->len);
+		if (unlikely(!mapping->base))
+			return -ENXIO;
+	} else
+		/* Existing mapping */
+		kref_get(&mapping->ref);
+
+	clk->mapping = mapping;
+	return 0;
+}
+
+static void clk_destroy_mapping(struct kref *kref)
+{
+	struct clk_mapping *mapping;
+
+	mapping = container_of(kref, struct clk_mapping, ref);
+
+	iounmap(mapping->base);
+}
+
+static void clk_teardown_mapping(struct clk *clk)
+{
+	struct clk_mapping *mapping = clk->mapping;
+
+	/* Nothing to do */
+	if (mapping == &dummy_mapping)
+		return;
+
+	kref_put(&mapping->ref, clk_destroy_mapping);
+	clk->mapping = NULL;
+}
+
 int clk_register(struct clk *clk)
 {
+	int ret;
+
 	if (clk == NULL || IS_ERR(clk))
 		return -EINVAL;
 
@@ -267,6 +344,10 @@  int clk_register(struct clk *clk)
 	INIT_LIST_HEAD(&clk->children);
 	clk->usecount = 0;
 
+	ret = clk_establish_mapping(clk);
+	if (unlikely(ret))
+		goto out_unlock;
+
 	if (clk->parent)
 		list_add(&clk->sibling, &clk->parent->children);
 	else
@@ -275,9 +356,11 @@  int clk_register(struct clk *clk)
 	list_add(&clk->node, &clock_list);
 	if (clk->ops && clk->ops->init)
 		clk->ops->init(clk);
+
+out_unlock:
 	mutex_unlock(&clock_list_sem);
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(clk_register);
 
@@ -286,6 +369,7 @@  void clk_unregister(struct clk *clk)
 	mutex_lock(&clock_list_sem);
 	list_del(&clk->sibling);
 	list_del(&clk->node);
+	clk_teardown_mapping(clk);
 	mutex_unlock(&clock_list_sem);
 }
 EXPORT_SYMBOL_GPL(clk_unregister);
diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h
index ecdfea5..8ae3770 100644
--- a/include/linux/sh_clk.h
+++ b/include/linux/sh_clk.h
@@ -4,11 +4,20 @@ 
 #include <linux/list.h>
 #include <linux/seq_file.h>
 #include <linux/cpufreq.h>
+#include <linux/types.h>
+#include <linux/kref.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 
 struct clk;
 
+struct clk_mapping {
+	phys_addr_t		phys;
+	void __iomem		*base;
+	unsigned long		len;
+	struct kref		ref;
+};
+
 struct clk_ops {
 	void (*init)(struct clk *clk);
 	int (*enable)(struct clk *clk);
@@ -42,6 +51,7 @@  struct clk {
 	unsigned long		arch_flags;
 	void			*priv;
 	struct dentry		*dentry;
+	struct clk_mapping	*mapping;
 	struct cpufreq_frequency_table *freq_table;
 };