瀏覽代碼

Merge branch 'clk-next-unregister' into clk-next

Conflicts:
	drivers/clk/clk.c
Mike Turquette 11 年之前
父節點
當前提交
dbdf6ff51e

+ 2 - 0
arch/arm/include/asm/clkdev.h

@@ -14,12 +14,14 @@
 
 
 #include <linux/slab.h>
 #include <linux/slab.h>
 
 
+#ifndef CONFIG_COMMON_CLK
 #ifdef CONFIG_HAVE_MACH_CLKDEV
 #ifdef CONFIG_HAVE_MACH_CLKDEV
 #include <mach/clkdev.h>
 #include <mach/clkdev.h>
 #else
 #else
 #define __clk_get(clk)	({ 1; })
 #define __clk_get(clk)	({ 1; })
 #define __clk_put(clk)	do { } while (0)
 #define __clk_put(clk)	do { } while (0)
 #endif
 #endif
+#endif
 
 
 static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 {
 {

+ 2 - 0
arch/blackfin/include/asm/clkdev.h

@@ -8,7 +8,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 	return kzalloc(size, GFP_KERNEL);
 	return kzalloc(size, GFP_KERNEL);
 }
 }
 
 
+#ifndef CONFIG_COMMON_CLK
 #define __clk_put(clk)
 #define __clk_put(clk)
 #define __clk_get(clk) ({ 1; })
 #define __clk_get(clk) ({ 1; })
+#endif
 
 
 #endif
 #endif

+ 2 - 0
arch/mips/include/asm/clkdev.h

@@ -14,8 +14,10 @@
 
 
 #include <linux/slab.h>
 #include <linux/slab.h>
 
 
+#ifndef CONFIG_COMMON_CLK
 #define __clk_get(clk)	({ 1; })
 #define __clk_get(clk)	({ 1; })
 #define __clk_put(clk)	do { } while (0)
 #define __clk_put(clk)	do { } while (0)
+#endif
 
 
 static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 {
 {

+ 2 - 0
arch/sh/include/asm/clkdev.h

@@ -25,7 +25,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 		return kzalloc(size, GFP_KERNEL);
 		return kzalloc(size, GFP_KERNEL);
 }
 }
 
 
+#ifndef CONFIG_COMMON_CLK
 #define __clk_put(clk)
 #define __clk_put(clk)
 #define __clk_get(clk) ({ 1; })
 #define __clk_get(clk) ({ 1; })
+#endif
 
 
 #endif /* __CLKDEV_H__ */
 #endif /* __CLKDEV_H__ */

+ 174 - 11
drivers/clk/clk.c

@@ -21,6 +21,8 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
 
 
+#include "clk.h"
+
 static DEFINE_SPINLOCK(enable_lock);
 static DEFINE_SPINLOCK(enable_lock);
 static DEFINE_MUTEX(prepare_lock);
 static DEFINE_MUTEX(prepare_lock);
 
 
@@ -350,6 +352,21 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
+ /**
+ * clk_debug_unregister - remove a clk node from the debugfs clk tree
+ * @clk: the clk being removed from the debugfs clk tree
+ *
+ * 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)
+{
+	debugfs_remove_recursive(clk->dentry);
+}
+
 /**
 /**
  * clk_debug_reparent - reparent clk node in the debugfs clk tree
  * clk_debug_reparent - reparent clk node in the debugfs clk tree
  * @clk: the clk being reparented
  * @clk: the clk being reparented
@@ -440,6 +457,9 @@ static inline int clk_debug_register(struct clk *clk) { return 0; }
 static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
 static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
 {
 {
 }
 }
+static inline void clk_debug_unregister(struct clk *clk)
+{
+}
 #endif
 #endif
 
 
 /* caller must hold prepare_lock */
 /* caller must hold prepare_lock */
@@ -1861,6 +1881,7 @@ int __clk_init(struct device *dev, struct clk *clk)
 	if (clk->ops->init)
 	if (clk->ops->init)
 		clk->ops->init(clk->hw);
 		clk->ops->init(clk->hw);
 
 
+	kref_init(&clk->ref);
 out:
 out:
 	clk_prepare_unlock();
 	clk_prepare_unlock();
 
 
@@ -1896,6 +1917,10 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw)
 	clk->flags = hw->init->flags;
 	clk->flags = hw->init->flags;
 	clk->parent_names = hw->init->parent_names;
 	clk->parent_names = hw->init->parent_names;
 	clk->num_parents = hw->init->num_parents;
 	clk->num_parents = hw->init->num_parents;
+	if (dev && dev->driver)
+		clk->owner = dev->driver->owner;
+	else
+		clk->owner = NULL;
 
 
 	ret = __clk_init(dev, clk);
 	ret = __clk_init(dev, clk);
 	if (ret)
 	if (ret)
@@ -1916,6 +1941,8 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
 		goto fail_name;
 		goto fail_name;
 	}
 	}
 	clk->ops = hw->init->ops;
 	clk->ops = hw->init->ops;
+	if (dev && dev->driver)
+		clk->owner = dev->driver->owner;
 	clk->hw = hw;
 	clk->hw = hw;
 	clk->flags = hw->init->flags;
 	clk->flags = hw->init->flags;
 	clk->num_parents = hw->init->num_parents;
 	clk->num_parents = hw->init->num_parents;
@@ -1990,13 +2017,104 @@ fail_out:
 }
 }
 EXPORT_SYMBOL_GPL(clk_register);
 EXPORT_SYMBOL_GPL(clk_register);
 
 
+/*
+ * Free memory allocated for a clock.
+ * Caller must hold prepare_lock.
+ */
+static void __clk_release(struct kref *ref)
+{
+	struct clk *clk = container_of(ref, struct clk, ref);
+	int i = clk->num_parents;
+
+	kfree(clk->parents);
+	while (--i >= 0)
+		kfree(clk->parent_names[i]);
+
+	kfree(clk->parent_names);
+	kfree(clk->name);
+	kfree(clk);
+}
+
+/*
+ * Empty clk_ops for unregistered clocks. These are used temporarily
+ * after clk_unregister() was called on a clock and until last clock
+ * consumer calls clk_put() and the struct clk object is freed.
+ */
+static int clk_nodrv_prepare_enable(struct clk_hw *hw)
+{
+	return -ENXIO;
+}
+
+static void clk_nodrv_disable_unprepare(struct clk_hw *hw)
+{
+	WARN_ON_ONCE(1);
+}
+
+static int clk_nodrv_set_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long parent_rate)
+{
+	return -ENXIO;
+}
+
+static int clk_nodrv_set_parent(struct clk_hw *hw, u8 index)
+{
+	return -ENXIO;
+}
+
+static const struct clk_ops clk_nodrv_ops = {
+	.enable		= clk_nodrv_prepare_enable,
+	.disable	= clk_nodrv_disable_unprepare,
+	.prepare	= clk_nodrv_prepare_enable,
+	.unprepare	= clk_nodrv_disable_unprepare,
+	.set_rate	= clk_nodrv_set_rate,
+	.set_parent	= clk_nodrv_set_parent,
+};
+
 /**
 /**
  * clk_unregister - unregister a currently registered clock
  * clk_unregister - unregister a currently registered clock
  * @clk: clock to unregister
  * @clk: clock to unregister
- *
- * Currently unimplemented.
  */
  */
-void clk_unregister(struct clk *clk) {}
+void clk_unregister(struct clk *clk)
+{
+	unsigned long flags;
+
+       if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
+               return;
+
+	clk_prepare_lock();
+
+	if (clk->ops == &clk_nodrv_ops) {
+		pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
+		goto out;
+	}
+	/*
+	 * Assign empty clock ops for consumers that might still hold
+	 * a reference to this clock.
+	 */
+	flags = clk_enable_lock();
+	clk->ops = &clk_nodrv_ops;
+	clk_enable_unlock(flags);
+
+	if (!hlist_empty(&clk->children)) {
+		struct clk *child;
+
+		/* Reparent all children to the orphan list. */
+		hlist_for_each_entry(child, &clk->children, child_node)
+			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);
 EXPORT_SYMBOL_GPL(clk_unregister);
 
 
 static void devm_clk_release(struct device *dev, void *res)
 static void devm_clk_release(struct device *dev, void *res)
@@ -2056,6 +2174,31 @@ void devm_clk_unregister(struct device *dev, struct clk *clk)
 }
 }
 EXPORT_SYMBOL_GPL(devm_clk_unregister);
 EXPORT_SYMBOL_GPL(devm_clk_unregister);
 
 
+/*
+ * clkdev helpers
+ */
+int __clk_get(struct clk *clk)
+{
+	if (clk && !try_module_get(clk->owner))
+		return 0;
+
+	kref_get(&clk->ref);
+	return 1;
+}
+
+void __clk_put(struct clk *clk)
+{
+	if (WARN_ON_ONCE(IS_ERR(clk)))
+		return;
+
+	clk_prepare_lock();
+	kref_put(&clk->ref, __clk_release);
+	clk_prepare_unlock();
+
+	if (clk)
+		module_put(clk->owner);
+}
+
 /***        clk rate change notifiers        ***/
 /***        clk rate change notifiers        ***/
 
 
 /**
 /**
@@ -2196,7 +2339,18 @@ static const struct of_device_id __clk_of_table_sentinel
 	__used __section(__clk_of_table_end);
 	__used __section(__clk_of_table_end);
 
 
 static LIST_HEAD(of_clk_providers);
 static LIST_HEAD(of_clk_providers);
-static DEFINE_MUTEX(of_clk_lock);
+static DEFINE_MUTEX(of_clk_mutex);
+
+/* of_clk_provider list locking helpers */
+void of_clk_lock(void)
+{
+	mutex_lock(&of_clk_mutex);
+}
+
+void of_clk_unlock(void)
+{
+	mutex_unlock(&of_clk_mutex);
+}
 
 
 struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
 struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
 				     void *data)
 				     void *data)
@@ -2240,9 +2394,9 @@ int of_clk_add_provider(struct device_node *np,
 	cp->data = data;
 	cp->data = data;
 	cp->get = clk_src_get;
 	cp->get = clk_src_get;
 
 
-	mutex_lock(&of_clk_lock);
+	mutex_lock(&of_clk_mutex);
 	list_add(&cp->link, &of_clk_providers);
 	list_add(&cp->link, &of_clk_providers);
-	mutex_unlock(&of_clk_lock);
+	mutex_unlock(&of_clk_mutex);
 	pr_debug("Added clock from %s\n", np->full_name);
 	pr_debug("Added clock from %s\n", np->full_name);
 
 
 	return 0;
 	return 0;
@@ -2257,7 +2411,7 @@ void of_clk_del_provider(struct device_node *np)
 {
 {
 	struct of_clk_provider *cp;
 	struct of_clk_provider *cp;
 
 
-	mutex_lock(&of_clk_lock);
+	mutex_lock(&of_clk_mutex);
 	list_for_each_entry(cp, &of_clk_providers, link) {
 	list_for_each_entry(cp, &of_clk_providers, link) {
 		if (cp->node == np) {
 		if (cp->node == np) {
 			list_del(&cp->link);
 			list_del(&cp->link);
@@ -2266,24 +2420,33 @@ void of_clk_del_provider(struct device_node *np)
 			break;
 			break;
 		}
 		}
 	}
 	}
-	mutex_unlock(&of_clk_lock);
+	mutex_unlock(&of_clk_mutex);
 }
 }
 EXPORT_SYMBOL_GPL(of_clk_del_provider);
 EXPORT_SYMBOL_GPL(of_clk_del_provider);
 
 
-struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
 {
 {
 	struct of_clk_provider *provider;
 	struct of_clk_provider *provider;
 	struct clk *clk = ERR_PTR(-ENOENT);
 	struct clk *clk = ERR_PTR(-ENOENT);
 
 
 	/* Check if we have such a provider in our array */
 	/* Check if we have such a provider in our array */
-	mutex_lock(&of_clk_lock);
 	list_for_each_entry(provider, &of_clk_providers, link) {
 	list_for_each_entry(provider, &of_clk_providers, link) {
 		if (provider->node == clkspec->np)
 		if (provider->node == clkspec->np)
 			clk = provider->get(clkspec, provider->data);
 			clk = provider->get(clkspec, provider->data);
 		if (!IS_ERR(clk))
 		if (!IS_ERR(clk))
 			break;
 			break;
 	}
 	}
-	mutex_unlock(&of_clk_lock);
+
+	return clk;
+}
+
+struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+{
+	struct clk *clk;
+
+	mutex_lock(&of_clk_mutex);
+	clk = __of_clk_get_from_provider(clkspec);
+	mutex_unlock(&of_clk_mutex);
 
 
 	return clk;
 	return clk;
 }
 }

+ 16 - 0
drivers/clk/clk.h

@@ -0,0 +1,16 @@
+/*
+ * linux/drivers/clk/clk.h
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@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.
+ */
+
+#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
+struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec);
+void of_clk_lock(void);
+void of_clk_unlock(void);
+#endif

+ 10 - 2
drivers/clk/clkdev.c

@@ -21,6 +21,8 @@
 #include <linux/clkdev.h>
 #include <linux/clkdev.h>
 #include <linux/of.h>
 #include <linux/of.h>
 
 
+#include "clk.h"
+
 static LIST_HEAD(clocks);
 static LIST_HEAD(clocks);
 static DEFINE_MUTEX(clocks_mutex);
 static DEFINE_MUTEX(clocks_mutex);
 
 
@@ -39,7 +41,13 @@ struct clk *of_clk_get(struct device_node *np, int index)
 	if (rc)
 	if (rc)
 		return ERR_PTR(rc);
 		return ERR_PTR(rc);
 
 
-	clk = of_clk_get_from_provider(&clkspec);
+	of_clk_lock();
+	clk = __of_clk_get_from_provider(&clkspec);
+
+	if (!IS_ERR(clk) && !__clk_get(clk))
+		clk = ERR_PTR(-ENOENT);
+
+	of_clk_unlock();
 	of_node_put(clkspec.np);
 	of_node_put(clkspec.np);
 	return clk;
 	return clk;
 }
 }
@@ -157,7 +165,7 @@ struct clk *clk_get(struct device *dev, const char *con_id)
 
 
 	if (dev) {
 	if (dev) {
 		clk = of_clk_get_by_name(dev->of_node, con_id);
 		clk = of_clk_get_by_name(dev->of_node, con_id);
-		if (!IS_ERR(clk) && __clk_get(clk))
+		if (!IS_ERR(clk))
 			return clk;
 			return clk;
 	}
 	}
 
 

+ 16 - 6
drivers/media/platform/omap3isp/isp.c

@@ -290,9 +290,11 @@ static int isp_xclk_init(struct isp_device *isp)
 	struct clk_init_data init;
 	struct clk_init_data init;
 	unsigned int i;
 	unsigned int i;
 
 
+	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i)
+		isp->xclks[i].clk = ERR_PTR(-EINVAL);
+
 	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
 	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
 		struct isp_xclk *xclk = &isp->xclks[i];
 		struct isp_xclk *xclk = &isp->xclks[i];
-		struct clk *clk;
 
 
 		xclk->isp = isp;
 		xclk->isp = isp;
 		xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B;
 		xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B;
@@ -305,10 +307,15 @@ static int isp_xclk_init(struct isp_device *isp)
 		init.num_parents = 1;
 		init.num_parents = 1;
 
 
 		xclk->hw.init = &init;
 		xclk->hw.init = &init;
-
-		clk = devm_clk_register(isp->dev, &xclk->hw);
-		if (IS_ERR(clk))
-			return PTR_ERR(clk);
+		/*
+		 * The first argument is NULL in order to avoid circular
+		 * reference, as this driver takes reference on the
+		 * sensor subdevice modules and the sensors would take
+		 * reference on this module through clk_get().
+		 */
+		xclk->clk = clk_register(NULL, &xclk->hw);
+		if (IS_ERR(xclk->clk))
+			return PTR_ERR(xclk->clk);
 
 
 		if (pdata->xclks[i].con_id == NULL &&
 		if (pdata->xclks[i].con_id == NULL &&
 		    pdata->xclks[i].dev_id == NULL)
 		    pdata->xclks[i].dev_id == NULL)
@@ -320,7 +327,7 @@ static int isp_xclk_init(struct isp_device *isp)
 
 
 		xclk->lookup->con_id = pdata->xclks[i].con_id;
 		xclk->lookup->con_id = pdata->xclks[i].con_id;
 		xclk->lookup->dev_id = pdata->xclks[i].dev_id;
 		xclk->lookup->dev_id = pdata->xclks[i].dev_id;
-		xclk->lookup->clk = clk;
+		xclk->lookup->clk = xclk->clk;
 
 
 		clkdev_add(xclk->lookup);
 		clkdev_add(xclk->lookup);
 	}
 	}
@@ -335,6 +342,9 @@ static void isp_xclk_cleanup(struct isp_device *isp)
 	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
 	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
 		struct isp_xclk *xclk = &isp->xclks[i];
 		struct isp_xclk *xclk = &isp->xclks[i];
 
 
+		if (!IS_ERR(xclk->clk))
+			clk_unregister(xclk->clk);
+
 		if (xclk->lookup)
 		if (xclk->lookup)
 			clkdev_drop(xclk->lookup);
 			clkdev_drop(xclk->lookup);
 	}
 	}

+ 1 - 0
drivers/media/platform/omap3isp/isp.h

@@ -135,6 +135,7 @@ struct isp_xclk {
 	struct isp_device *isp;
 	struct isp_device *isp;
 	struct clk_hw hw;
 	struct clk_hw hw;
 	struct clk_lookup *lookup;
 	struct clk_lookup *lookup;
+	struct clk *clk;
 	enum isp_xclk_id id;
 	enum isp_xclk_id id;
 
 
 	spinlock_t lock;	/* Protects enabled and divider */
 	spinlock_t lock;	/* Protects enabled and divider */

+ 5 - 0
include/linux/clk-private.h

@@ -12,6 +12,7 @@
 #define __LINUX_CLK_PRIVATE_H
 #define __LINUX_CLK_PRIVATE_H
 
 
 #include <linux/clk-provider.h>
 #include <linux/clk-provider.h>
+#include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/list.h>
 
 
 /*
 /*
@@ -25,10 +26,13 @@
 
 
 #ifdef CONFIG_COMMON_CLK
 #ifdef CONFIG_COMMON_CLK
 
 
+struct module;
+
 struct clk {
 struct clk {
 	const char		*name;
 	const char		*name;
 	const struct clk_ops	*ops;
 	const struct clk_ops	*ops;
 	struct clk_hw		*hw;
 	struct clk_hw		*hw;
+	struct module		*owner;
 	struct clk		*parent;
 	struct clk		*parent;
 	const char		**parent_names;
 	const char		**parent_names;
 	struct clk		**parents;
 	struct clk		**parents;
@@ -48,6 +52,7 @@ struct clk {
 #ifdef CONFIG_DEBUG_FS
 #ifdef CONFIG_DEBUG_FS
 	struct dentry		*dentry;
 	struct dentry		*dentry;
 #endif
 #endif
+	struct kref		ref;
 };
 };
 
 
 /*
 /*

+ 5 - 0
include/linux/clkdev.h

@@ -43,4 +43,9 @@ int clk_add_alias(const char *, const char *, char *, struct device *);
 int clk_register_clkdev(struct clk *, const char *, const char *, ...);
 int clk_register_clkdev(struct clk *, const char *, const char *, ...);
 int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t);
 int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t);
 
 
+#ifdef CONFIG_COMMON_CLK
+int __clk_get(struct clk *clk);
+void __clk_put(struct clk *clk);
+#endif
+
 #endif
 #endif