Loading arch/arm/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,12 +14,14 @@ #include <linux/slab.h> #ifndef CONFIG_COMMON_CLK #ifdef CONFIG_HAVE_MACH_CLKDEV #include <mach/clkdev.h> #else #define __clk_get(clk) ({ 1; }) #define __clk_put(clk) do { } while (0) #endif #endif static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) { Loading arch/blackfin/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -8,7 +8,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) return kzalloc(size, GFP_KERNEL); } #ifndef CONFIG_COMMON_CLK #define __clk_put(clk) #define __clk_get(clk) ({ 1; }) #endif #endif arch/mips/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,8 +14,10 @@ #include <linux/slab.h> #ifndef CONFIG_COMMON_CLK #define __clk_get(clk) ({ 1; }) #define __clk_put(clk) do { } while (0) #endif static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) { Loading arch/sh/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) return kzalloc(size, GFP_KERNEL); } #ifndef CONFIG_COMMON_CLK #define __clk_put(clk) #define __clk_get(clk) ({ 1; }) #endif #endif /* __CLKDEV_H__ */ drivers/clk/clk.c +174 −11 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <linux/init.h> #include <linux/sched.h> #include "clk.h" static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); Loading Loading @@ -343,6 +345,21 @@ static int clk_debug_register(struct clk *clk) 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: the clk being reparented Loading Loading @@ -433,6 +450,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_unregister(struct clk *clk) { } #endif /* caller must hold prepare_lock */ Loading Loading @@ -1776,6 +1796,7 @@ int __clk_init(struct device *dev, struct clk *clk) clk_debug_register(clk); kref_init(&clk->ref); out: clk_prepare_unlock(); Loading Loading @@ -1811,6 +1832,10 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw) clk->flags = hw->init->flags; clk->parent_names = hw->init->parent_names; 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); if (ret) Loading @@ -1831,6 +1856,8 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) goto fail_name; } clk->ops = hw->init->ops; if (dev && dev->driver) clk->owner = dev->driver->owner; clk->hw = hw; clk->flags = hw->init->flags; clk->num_parents = hw->init->num_parents; Loading Loading @@ -1905,13 +1932,104 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) } 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: 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); static void devm_clk_release(struct device *dev, void *res) Loading Loading @@ -1971,6 +2089,31 @@ void devm_clk_unregister(struct device *dev, struct clk *clk) } 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 ***/ /** Loading Loading @@ -2111,7 +2254,18 @@ static const struct of_device_id __clk_of_table_sentinel __used __section(__clk_of_table_end); 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, void *data) Loading Loading @@ -2155,9 +2309,9 @@ int of_clk_add_provider(struct device_node *np, cp->data = data; cp->get = clk_src_get; mutex_lock(&of_clk_lock); mutex_lock(&of_clk_mutex); 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); return 0; Loading @@ -2172,7 +2326,7 @@ void of_clk_del_provider(struct device_node *np) { struct of_clk_provider *cp; mutex_lock(&of_clk_lock); mutex_lock(&of_clk_mutex); list_for_each_entry(cp, &of_clk_providers, link) { if (cp->node == np) { list_del(&cp->link); Loading @@ -2181,24 +2335,33 @@ void of_clk_del_provider(struct device_node *np) break; } } mutex_unlock(&of_clk_lock); mutex_unlock(&of_clk_mutex); } 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 clk *clk = ERR_PTR(-ENOENT); /* Check if we have such a provider in our array */ mutex_lock(&of_clk_lock); list_for_each_entry(provider, &of_clk_providers, link) { if (provider->node == clkspec->np) clk = provider->get(clkspec, provider->data); if (!IS_ERR(clk)) 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; } Loading Loading
arch/arm/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,12 +14,14 @@ #include <linux/slab.h> #ifndef CONFIG_COMMON_CLK #ifdef CONFIG_HAVE_MACH_CLKDEV #include <mach/clkdev.h> #else #define __clk_get(clk) ({ 1; }) #define __clk_put(clk) do { } while (0) #endif #endif static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) { Loading
arch/blackfin/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -8,7 +8,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) return kzalloc(size, GFP_KERNEL); } #ifndef CONFIG_COMMON_CLK #define __clk_put(clk) #define __clk_get(clk) ({ 1; }) #endif #endif
arch/mips/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,8 +14,10 @@ #include <linux/slab.h> #ifndef CONFIG_COMMON_CLK #define __clk_get(clk) ({ 1; }) #define __clk_put(clk) do { } while (0) #endif static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) { Loading
arch/sh/include/asm/clkdev.h +2 −0 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) return kzalloc(size, GFP_KERNEL); } #ifndef CONFIG_COMMON_CLK #define __clk_put(clk) #define __clk_get(clk) ({ 1; }) #endif #endif /* __CLKDEV_H__ */
drivers/clk/clk.c +174 −11 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <linux/init.h> #include <linux/sched.h> #include "clk.h" static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); Loading Loading @@ -343,6 +345,21 @@ static int clk_debug_register(struct clk *clk) 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: the clk being reparented Loading Loading @@ -433,6 +450,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_unregister(struct clk *clk) { } #endif /* caller must hold prepare_lock */ Loading Loading @@ -1776,6 +1796,7 @@ int __clk_init(struct device *dev, struct clk *clk) clk_debug_register(clk); kref_init(&clk->ref); out: clk_prepare_unlock(); Loading Loading @@ -1811,6 +1832,10 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw) clk->flags = hw->init->flags; clk->parent_names = hw->init->parent_names; 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); if (ret) Loading @@ -1831,6 +1856,8 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) goto fail_name; } clk->ops = hw->init->ops; if (dev && dev->driver) clk->owner = dev->driver->owner; clk->hw = hw; clk->flags = hw->init->flags; clk->num_parents = hw->init->num_parents; Loading Loading @@ -1905,13 +1932,104 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) } 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: 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); static void devm_clk_release(struct device *dev, void *res) Loading Loading @@ -1971,6 +2089,31 @@ void devm_clk_unregister(struct device *dev, struct clk *clk) } 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 ***/ /** Loading Loading @@ -2111,7 +2254,18 @@ static const struct of_device_id __clk_of_table_sentinel __used __section(__clk_of_table_end); 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, void *data) Loading Loading @@ -2155,9 +2309,9 @@ int of_clk_add_provider(struct device_node *np, cp->data = data; cp->get = clk_src_get; mutex_lock(&of_clk_lock); mutex_lock(&of_clk_mutex); 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); return 0; Loading @@ -2172,7 +2326,7 @@ void of_clk_del_provider(struct device_node *np) { struct of_clk_provider *cp; mutex_lock(&of_clk_lock); mutex_lock(&of_clk_mutex); list_for_each_entry(cp, &of_clk_providers, link) { if (cp->node == np) { list_del(&cp->link); Loading @@ -2181,24 +2335,33 @@ void of_clk_del_provider(struct device_node *np) break; } } mutex_unlock(&of_clk_lock); mutex_unlock(&of_clk_mutex); } 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 clk *clk = ERR_PTR(-ENOENT); /* Check if we have such a provider in our array */ mutex_lock(&of_clk_lock); list_for_each_entry(provider, &of_clk_providers, link) { if (provider->node == clkspec->np) clk = provider->get(clkspec, provider->data); if (!IS_ERR(clk)) 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; } Loading