diff options
Diffstat (limited to 'recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch')
-rw-r--r-- | recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch new file mode 100644 index 0000000000..c09c208c6a --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch @@ -0,0 +1,446 @@ +From 62c9a23cfa7181369637d0b61a8e90c83c562f03 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 03:01:06 +0300 +Subject: [PATCH 39/64] Add generic framework for managing clocks. + +Provide a generic framework that platform may choose +to support clocks api. In particular this provides +platform-independant struct clk definition, a full +implementation of clocks api and a set of functions +for registering and unregistering clocks in a safe way. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + include/linux/clklib.h | 85 ++++++++++++++ + init/Kconfig | 7 + + kernel/Makefile | 1 + + kernel/clklib.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 388 insertions(+), 0 deletions(-) + create mode 100644 include/linux/clklib.h + create mode 100644 kernel/clklib.c + +diff --git a/include/linux/clklib.h b/include/linux/clklib.h +new file mode 100644 +index 0000000..4bd9b4a +--- /dev/null ++++ b/include/linux/clklib.h +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (C) 2008 Dmitry Baryshkov ++ * ++ * This file is released under the GPL v2. ++ */ ++ ++#ifndef CLKLIB_H ++#define CLKLIB_H ++ ++#include <linux/list.h> ++ ++struct clk { ++ struct list_head node; ++ struct clk *parent; ++ ++ const char *name; ++ struct module *owner; ++ ++ int users; ++ unsigned long rate; ++ int delay; ++ ++ int (*can_get) (struct clk *, struct device *); ++ int (*set_parent) (struct clk *, struct clk *); ++ int (*enable) (struct clk *); ++ void (*disable) (struct clk *); ++ unsigned long (*getrate) (struct clk*); ++ int (*setrate) (struct clk *, unsigned long); ++ long (*roundrate) (struct clk *, unsigned long); ++ ++ void *priv; ++}; ++ ++int clk_register(struct clk *clk); ++void clk_unregister(struct clk *clk); ++static void __maybe_unused clks_register(struct clk *clks, size_t num) ++{ ++ int i; ++ for (i = 0; i < num; i++) { ++ clk_register(&clks[i]); ++ } ++} ++ ++ ++int clk_alloc_function(const char *parent, struct clk *clk); ++ ++struct clk_function { ++ const char *parent; ++ struct clk *clk; ++}; ++ ++#define CLK_FUNC(_clock, _function, _can_get, _data, _format) \ ++ { \ ++ .parent = _clock, \ ++ .clk = &(struct clk) { \ ++ .name= _function, \ ++ .owner = THIS_MODULE, \ ++ .can_get = _can_get, \ ++ .priv = _data, \ ++ .format = _format, \ ++ }, \ ++ } ++ ++static int __maybe_unused clk_alloc_functions( ++ struct clk_function *funcs, ++ int num) ++{ ++ int i; ++ int rc; ++ ++ for (i = 0; i < num; i++) { ++ rc = clk_alloc_function(funcs[i].parent, funcs[i].clk); ++ ++ if (rc) { ++ printk(KERN_ERR "Error allocating %s.%s function.\n", ++ funcs[i].parent, ++ funcs[i].clk->name); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++#endif +diff --git a/init/Kconfig b/init/Kconfig +index b9d11a8..05b62ba 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -435,6 +435,13 @@ config CC_OPTIMIZE_FOR_SIZE + config SYSCTL + bool + ++config HAVE_CLOCK_LIB ++ bool ++ help ++ Platforms select clocklib if they use this infrastructure ++ for managing their clocks both built into SoC and provided ++ by external devices. ++ + menuconfig EMBEDDED + bool "Configure standard kernel features (for small systems)" + help +diff --git a/kernel/Makefile b/kernel/Makefile +index 6d9a87c..0b2ade7 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -58,6 +58,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o + obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o + obj-$(CONFIG_MARKERS) += marker.o + obj-$(CONFIG_LATENCYTOP) += latencytop.o ++obj-$(CONFIG_HAVE_CLOCK_LIB) += clklib.o + + ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) + # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is +diff --git a/kernel/clklib.c b/kernel/clklib.c +new file mode 100644 +index 0000000..203af3d +--- /dev/null ++++ b/kernel/clklib.c +@@ -0,0 +1,295 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/clklib.h> ++#include <linux/spinlock.h> ++#include <linux/err.h> ++#include <linux/delay.h> ++ ++static LIST_HEAD(clocks); ++static DEFINE_SPINLOCK(clocks_lock); ++ ++static int __clk_register(struct clk *clk) ++{ ++ if (clk->parent && ++ !try_module_get(clk->parent->owner)) ++ return -EINVAL; ++ ++ list_add_tail(&clk->node, &clocks); ++ ++ return 0; ++} ++ ++int clk_register(struct clk *clk) ++{ ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = __clk_register(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_register); ++ ++void clk_unregister(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ list_del(&clk->node); ++ if (clk->parent) ++ module_put(clk->parent->owner); ++ spin_unlock_irqrestore(&clocks_lock, flags); ++} ++EXPORT_SYMBOL(clk_unregister); ++ ++struct clk *clk_get(struct device *dev, const char *id) ++{ ++ struct clk *p, *clk = ERR_PTR(-ENOENT); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ list_for_each_entry(p, &clocks, node) { ++ if (strcmp(id, p->name) == 0 && ++ (!p->can_get || p->can_get(p, dev)) && ++ try_module_get(p->owner)) { ++ clk = p; ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return clk; ++} ++EXPORT_SYMBOL(clk_get); ++ ++void clk_put(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ module_put(clk->owner); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++} ++EXPORT_SYMBOL(clk_put); ++ ++int clk_set_parent(struct clk *clk, struct clk *parent) ++{ ++ int rc; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ if (!clk->set_parent) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = clk->set_parent(clk, parent); ++ if (!rc) ++ clk->parent = parent; ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_set_parent); ++ ++static int __clk_enable(struct clk *clk) ++{ ++ int rc = 0; ++ ++ if (clk->parent) { ++ rc = __clk_enable(clk->parent); ++ ++ if (rc) ++ return rc; ++ } ++ ++ if (clk->users++ == 0) ++ if (clk->enable) ++ rc = clk->enable(clk); ++ ++ if (clk->delay) ++ udelay(clk->delay); ++ ++ return rc; ++} ++ ++int clk_enable(struct clk *clk) ++{ ++ unsigned long flags; ++ int rc; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = __clk_enable(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++static void __clk_disable(struct clk *clk) ++{ ++ if (clk->users <= 0) { ++ WARN_ON(1); ++ return; ++ } ++ ++ if (--clk->users == 0) ++ if (clk->disable) ++ clk->disable(clk); ++ ++ if (clk->parent) ++ __clk_disable(clk->parent); ++} ++ ++void clk_disable(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ __clk_disable(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++} ++EXPORT_SYMBOL(clk_disable); ++ ++static unsigned long __clk_get_rate(struct clk *clk) ++{ ++ unsigned long rate = 0; ++ ++ for (;;) { ++ if (rate || !clk) ++ return rate; ++ ++ if (clk->getrate) ++ rate = clk->getrate(clk); ++ else if (clk->rate) ++ rate = clk->rate; ++ else ++ clk = clk->parent; ++ } ++} ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ unsigned long rate = 0; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rate = __clk_get_rate(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rate; ++} ++EXPORT_SYMBOL(clk_get_rate); ++ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ long res; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ if (!clk->roundrate) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ res = clk->roundrate(clk, rate); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return res; ++} ++EXPORT_SYMBOL(clk_round_rate); ++ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ int rc; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ if (!clk->setrate) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = clk->setrate(clk, rate); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_set_rate); ++ ++int clk_alloc_function(const char *parent, struct clk *clk) ++{ ++ int rc = 0; ++ unsigned long flags; ++ struct clk *pclk; ++ bool found = false; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ list_for_each_entry(pclk, &clocks, node) { ++ if (strcmp(parent, pclk->name) == 0 && ++ try_module_get(pclk->owner)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ clk->parent = pclk; ++ ++ __clk_register(clk); ++ /* ++ * We locked parent owner during search ++ * and also in __clk_register. Free one reference ++ */ ++ module_put(pclk->owner); ++ ++out: ++ if (rc) { ++ kfree(clk); ++ } ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_alloc_function); +-- +1.5.3.8 + |