Embedded Linux development
 help / color / mirror / Atom feed
* Re: [Proposal] [PATCH] generic clock framework
From: Francesco VIRLINZI @ 2009-11-11  7:54 UTC (permalink / raw)
  To: Tim Bird; +Cc: Linux-sh, linux-embedded, linux-arm-kernel
In-Reply-To: <4AF9AC3B.2070004@am.sony.com>

Hi Tim
Thanks for your feedback.
ARM mail list was already involved.

Regards
  Francesco
On 11/10/2009 07:08 PM, Tim Bird wrote:
> Francesco VIRLINZI wrote:
>    
>> Hi all
>>
>> I'm Francesco and I work in STMicroelectronics
>>
>> In the last ELC-E_2009 I spoke on a generic clock framework I'm working on
>>   (see
>> http://tree.celinuxforum.org/CelfPubWiki/ELCEurope2009Presentations?action=AttachFile&do=view&target=ELC_E_2009_Generic_Clock_Framework.pdf).
>>
>>
>> I wrote the gcf to manage both clocks the platform_devices during a
>> clock operation.
>>      
> This looks good to me, in principle, but I'm not a clock or PM
> expert.  I would recommend sending this to the linux-kernel and
> linux-pm lists as well.  I think you'll get a wider audience for
> feedback.
>   -- Tim
>
> =============================
> Tim Bird
> Architecture Group Chair, CE Linux Forum
> Senior Staff Engineer, Sony Corporation of America
> =============================
>
>
>    

^ permalink raw reply

* New Micron e-MMC support...
From: CYR Burt @ 2009-11-10 21:46 UTC (permalink / raw)
  To: linux-embedded, linux-mmc


Hi,

I was looking to see if the Micron eMMC 2/4GB devices are currently
supported in the latest stable kernel (31.6). It looks like Russell King
did the MMC core work, but I'm not sure if any testing has been done on
the Micron 2/4GB models which are currently sampling.

Any insights would be greatly appreciated.

Thanks


Burt Cyr
Alcatel-Lucent

^ permalink raw reply

* Re: [Proposal] [PATCH] generic clock framework
From: Tim Bird @ 2009-11-10 18:08 UTC (permalink / raw)
  To: Francesco VIRLINZI; +Cc: Linux-sh, linux-embedded
In-Reply-To: <4AF98175.4090205@st.com>

Francesco VIRLINZI wrote:
> Hi all
> 
> I'm Francesco and I work in STMicroelectronics
> 
> In the last ELC-E_2009 I spoke on a generic clock framework I'm working on
>  (see
> http://tree.celinuxforum.org/CelfPubWiki/ELCEurope2009Presentations?action=AttachFile&do=view&target=ELC_E_2009_Generic_Clock_Framework.pdf).
> 
> 
> I wrote the gcf to manage both clocks the platform_devices during a
> clock operation.

This looks good to me, in principle, but I'm not a clock or PM
expert.  I would recommend sending this to the linux-kernel and
linux-pm lists as well.  I think you'll get a wider audience for
feedback.
 -- Tim

=============================
Tim Bird
Architecture Group Chair, CE Linux Forum
Senior Staff Engineer, Sony Corporation of America
=============================


^ permalink raw reply

* [Proposal] [PATCH] generic clock framework
From: Francesco VIRLINZI @ 2009-11-10 15:06 UTC (permalink / raw)
  To: Linux-sh, linux-embedded

[-- Attachment #1: Type: text/plain, Size: 1842 bytes --]

Hi all

I'm Francesco and I work in STMicroelectronics

In the last ELC-E_2009 I spoke on a generic clock framework I'm working on
  (see 
http://tree.celinuxforum.org/CelfPubWiki/ELCEurope2009Presentations?action=AttachFile&do=view&target=ELC_E_2009_Generic_Clock_Framework.pdf).

I wrote the gcf to manage both clocks the platform_devices during a 
clock operation.

The main features are:
  - it's integrated in the LDM
  - it tracks the clock-to-clock relationship
  - it tracks the clock-to-device relationship

  - it has sysfs interface
  - - the user can navigate the clock tree under /sys/clocks/...

  - it uses the linux API (<linux/clk.h>) with some extra functions (to 
register/unregister a clock
    and other utility functions as clk_for_each())

  - it involves the platform_device and the platform_driver in the clock 
propagation.
  - - basically each clock operation is managed as a transaction which 
evolves step by step.
  - - all the clock rates are evaluated (before the clk operation is 
actually done)
  - - each platform_device can check (before the clk operation is 
actually done) the clk environment
      it will have at the end of clock operation and if required it can 
reject the operation.
  - - each clock operation is actually executed only if all the 
platform_devices accept the operation it-self


Moreover a common clock framework could be used to avoid a lot of 
duplicated and/or similar code
  just a grep of 'EXPORT_SYMBOL\(clk_enable' under arch/arm finds 22 
entries.

The patch is based on a 2.6.30 kernel also if it has a preliminary 
integration with the PM_RUNTIME
  support.

It works on our st40 (an sh4 cpu based system) no test/porting was done 
on any ARM platform.

It would be mainly a starting point for a discussion and I'm available 
to extend/fix/share it.

Regards
  Francesco


[-- Attachment #2: 0001-generic-clock-framework.patch --]
[-- Type: text/plain, Size: 77606 bytes --]

From 4e065fb9247ec511bfdc88001f0713977d3f4e89 Mon Sep 17 00:00:00 2001
From: Francesco Virlinzi <francesco.virlinzi@st.com>
Date: Fri, 23 Oct 2009 15:26:42 +0200
Subject: [PATCH] generic clock framework

version: 0.6.2

Signed-off-by: Francesco Virlinzi <francesco.virlinzi@st.com>
---
 drivers/base/Makefile           |    4 +
 drivers/base/base.h             |    5 +
 drivers/base/clk.c              | 1606 +++++++++++++++++++++++++++++++++++++++
 drivers/base/clk.h              |  319 ++++++++
 drivers/base/clk_pm.c           |  197 +++++
 drivers/base/clk_utils.c        |  456 +++++++++++
 drivers/base/init.c             |    1 +
 drivers/base/platform.c         |   27 +
 include/linux/clk.h             |  251 ++++++
 include/linux/platform_device.h |    9 +
 init/Kconfig                    |   23 +
 11 files changed, 2898 insertions(+), 0 deletions(-)
 create mode 100644 drivers/base/clk.c
 create mode 100644 drivers/base/clk.h
 create mode 100644 drivers/base/clk_pm.c
 create mode 100644 drivers/base/clk_utils.c

diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index b5b8ba5..b78a2bf 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -16,6 +16,10 @@ ifeq ($(CONFIG_SYSFS),y)
 obj-$(CONFIG_MODULES)	+= module.o
 endif
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+ifdef CONFIG_GENERIC_CLK_FM
+obj-y			+= clk.o clk_utils.o
+obj-$(CONFIG_PM)	+= clk_pm.o
+endif
 
 ifeq ($(CONFIG_DEBUG_DRIVER),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/base/base.h b/drivers/base/base.h
index b528145..bc5b9e8 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -94,6 +94,11 @@ extern int devices_init(void);
 extern int buses_init(void);
 extern int classes_init(void);
 extern int firmware_init(void);
+#ifdef CONFIG_GENERIC_CLK_FM
+extern int clock_init(void);
+#else
+static inline int clock_init(void){ return 0; }
+#endif
 #ifdef CONFIG_SYS_HYPERVISOR
 extern int hypervisor_init(void);
 #else
diff --git a/drivers/base/clk.c b/drivers/base/clk.c
new file mode 100644
index 0000000..7feae61
--- /dev/null
+++ b/drivers/base/clk.c
@@ -0,0 +1,1606 @@
+/*
+ * -------------------------------------------------------------------------
+ * clk.c
+ * -------------------------------------------------------------------------
+ * (C) STMicroelectronics 2008
+ * (C) STMicroelectronics 2009
+ * Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+ * -------------------------------------------------------------------------
+ * May be copied or modified under the terms of the GNU General Public
+ * License v.2 ONLY.  See linux/COPYING for more information.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/klist.h>
+#include <linux/sysdev.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include "clk.h"
+#include "base.h"
+
+#define CLK_NAME		"Generic Clk Framework"
+#define CLK_VERSION		"0.6.2"
+
+/* #define CLK_SAFE_CODE */
+
+klist_entry_support(clock, clk, node)
+klist_entry_support(child_clock, clk, child_node)
+klist_entry_support(dev_info, pdev_clk_info, node)
+
+#define to_clk(ptr)	container_of(ptr, struct clk, kobj)
+#define to_tnode(ptr)	container_of(ptr, struct clk_tnode, pnode)
+
+static int sysfs_clk_attr_show(struct kobject *kobj,
+				struct attribute *attr, char *buf)
+{
+	ssize_t ret = -EIO;
+	struct kobj_attribute *kattr
+	    = container_of(attr, struct kobj_attribute, attr);
+	if (kattr->show)
+		ret = kattr->show(kobj, kattr, buf);
+	return ret;
+}
+
+static ssize_t
+sysfs_clk_attr_store(struct kobject *kobj, struct attribute *attr,
+			const char *buf, size_t count)
+{
+	ssize_t ret = -EIO;
+	struct kobj_attribute *kattr
+	    = container_of(attr, struct kobj_attribute, attr);
+	if (kattr->store)
+		ret = kattr->store(kobj, kattr, buf, count);
+	return ret;
+}
+
+static struct sysfs_ops clk_sysfs_ops = {
+	.show = sysfs_clk_attr_show,
+	.store = sysfs_clk_attr_store,
+};
+
+static struct kobj_type ktype_clk = {
+	.sysfs_ops = &clk_sysfs_ops,
+};
+
+static struct clk *check_clk(struct clk *);
+
+static struct kobject *clk_kobj;
+static DEFINE_MUTEX(clk_list_sem);
+static atomic_t transaction_counter = ATOMIC_INIT(0);
+struct klist clk_list = KLIST_INIT(clk_list, NULL, NULL);
+
+klist_function_support(child, clk, child_node, kobj)
+klist_function_support(device, pdev_clk_info, node, pdev->dev.kobj)
+
+/*
+ * The ___clk_xxx operations doesn't raise propagation
+ * they are used to operate on the real clock
+ */
+static int
+__clk_operations(struct clk *clk, unsigned long rate,
+	enum clk_ops_id const id_ops)
+{
+	int ret = 0;
+	unsigned long *ops_fns = (unsigned long *)clk->ops;
+	if (likely(ops_fns && ops_fns[id_ops])) {
+		int (*fns)(struct clk *clk, unsigned long rate)
+			= (void *)ops_fns[id_ops];
+		unsigned long flags;
+		spin_lock_irqsave(&clk->lock, flags);
+		ret = fns(clk, rate);
+		spin_unlock_irqrestore(&clk->lock, flags);
+	}
+	return ret;
+}
+
+static inline int __clk_init(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_INIT);
+}
+static inline int __clk_enable(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_ENABLE);
+}
+static inline int __clk_disable(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_DISABLE);
+}
+static inline int __clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, rate, __CLK_SET_RATE);
+}
+static inline int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, (unsigned long)parent, __CLK_SET_PARENT);
+}
+static inline int __clk_recalc_rate(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_RECALC);
+}
+static inline int __clk_round(struct clk *clk, unsigned long value)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, value, __CLK_ROUND);
+}
+
+static inline int __clk_eval(struct clk *clk, unsigned long prate)
+{
+#ifndef CONFIG_CLK_FORCE_GENERIC_EVALUATE
+	pr_debug(": %s\n", clk->name);
+	return	__clk_operations(clk, prate, __CLK_EVAL);
+#else
+	unsigned long rate, flags;
+	pr_debug(": %s\n", clk->name);
+	if (likely(clk->ops && clk->ops->eval)) {
+		spin_lock_irqsave(&clk->lock, flags);
+		rate = clk->ops->eval(clk, prate);
+		spin_unlock_irqrestore(&clk->lock, flags);
+	} else
+		rate = clk_generic_evaluate_rate(clk, prate);
+	return rate;
+#endif
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int
+clk_pm_runtime_devinfo(enum rpm_status code, struct pdev_clk_info *info)
+{
+	struct platform_device *pdev = info->pdev;
+
+	pr_debug("\n");
+
+	switch (code) {
+	case RPM_ACTIVE:
+		return clk_notify_child_event(CHILD_DEVICE_ENABLED, info->clk);
+	case RPM_SUSPENDED:
+		return clk_notify_child_event(CHILD_DEVICE_DISABLED, info->clk);
+	}
+	return -EINVAL;
+}
+
+int clk_pm_runtime_device(enum rpm_status code, struct platform_device *dev)
+{
+	int idx;
+	int ret = 0;
+	struct pdev_clk_info *info;
+
+	if (!dev)
+		return -EFAULT;
+
+	if (!dev->clks || !pdevice_num_clocks(dev))
+		return 0;
+
+	pr_debug("\n");
+/*
+ *	Check if the device is under a transaction.
+ * 	If so the GCFdoesn't raise a 'clk_pm_runtime_devinfo'
+ *	all the device change will be notified on 'tnode_transaction_complete'
+ *	if required....
+ */
+	if (atomic_read((atomic_t *)&dev->clk_flags)) {
+		pr_debug("%s.%d under transaction\n", dev->name, dev->id);
+		return ret;
+	}
+	for (idx = 0, info = dev->clks; idx < pdevice_num_clocks(dev); ++idx)
+		ret |= clk_pm_runtime_devinfo(&info[idx], state, 0);
+
+	return ret;
+}
+#else
+#define clk_pm_runtime_devinfo(x, y)
+#define clk_pm_runtime_device(x, y)
+#endif
+
+/**
+ * tnode_malloc
+ *
+ * Allocs the memory for both the transaction and the
+ * clk_event objects
+ */
+static struct clk_tnode *tnode_malloc(struct clk_tnode *parent,
+	unsigned long nevent)
+{
+	struct clk_event *evt;
+	struct clk_tnode *node;
+
+	if (nevent > 32)
+		return NULL;
+
+	node = kmalloc(sizeof(*node) + nevent *	sizeof(*evt), GFP_KERNEL);
+
+	if (!node)
+		return NULL;
+
+	evt = (struct clk_event *)(sizeof(struct clk_tnode) + (long)node);
+
+	node->tid    = atomic_inc_return(&transaction_counter);
+	node->parent = parent;
+	node->size   = nevent;
+	node->events = evt;
+	node->events_map = 0;
+	INIT_LIST_HEAD(&node->childs);
+
+	return node;
+}
+
+/**
+ * tnode_free
+ *
+ * Free the tnode memory
+ */
+static void tnode_free(struct clk_tnode *node)
+{
+	if (tnode_get_parent(node)) {
+		list_del(&node->pnode);
+		kfree(node);
+	}
+}
+
+/**
+ *  tnode_check_clock -
+ *
+ *  @node: the tnode object
+ *  @clk:  the clock object
+ *
+ *  returns a boolean value
+ *  it checks if the clock (clk) is managed by the
+ *  tnode (node) or any parent node
+ */
+static int __must_check
+tnode_check_clock(struct clk_tnode *node, struct clk *clk)
+{
+	int j;
+	for (; node; node = tnode_get_parent(node))
+		/* scans all the event */
+		tnode_for_each_valid_events(node, j)
+			if (tnode_get_clock(node, j) == clk)
+					return 1; /* FOUND!!! */
+	return 0;
+}
+
+/**
+  * tnode_lock_clocks -
+  *
+  * @node: the tnode object
+  *
+  * marks all the clocks under transaction to be sure there is no more
+  * than one transaction for each clock
+  */
+static int __must_check
+tnode_lock_clocks(struct clk_tnode *node)
+{
+	int i;
+	pr_debug("\n");
+
+	/* 1. try to mark all the clocks in transaction */
+	for (i = 0; i < tnode_get_size(node); ++i)
+		if (clk_set_towner(tnode_get_clock(node, i), node)) {
+			struct clk *clkp = tnode_get_clock(node, i);
+			/* this clock is already locked */
+			/* we accept that __only__ if it is locked by a
+			 * parent tnode!!!
+			 */
+			if (!tnode_get_parent(node)) {
+				pr_debug("Error clk %s locked but "
+					  "there is no parent!\n", clkp->name);
+				goto err_0;
+			}
+			pr_debug("clk %s already locked\n", clkp->name);
+			if (tnode_check_clock(tnode_get_parent(node), clkp)) {
+				pr_debug("ok clk %s locked "
+					  "by a parent\n", clkp->name);
+				continue;
+			} else
+				goto err_0;
+		} else
+			/* set the event as valid in the bitmap*/
+			tnode_set_map_id(node, i);
+
+/*
+ * all the clocks are marked succesfully or all the clock on
+ * this tnode are already managed by parent
+ */
+	if (!tnode_get_map(node)) { /* check if the bitamp is not zero */
+		if (tnode_get_parent(node))
+			kfree(node);
+		return 1;
+	}
+
+ /*
+ * all the clocks are marked succesfully _and_ there is at least
+ * one clock marked.
+ * Add the tnode to its parent! and return
+ */
+	if (tnode_get_parent(node))
+		list_add_tail(&node->pnode, &tnode_get_parent(node)->childs);
+
+	return 0;
+
+err_0:
+	pr_debug("Error on clock locking...\n");
+	for (--i; i >= 0; --i)
+		if (tnode_check_map_id(node, i))
+			clk_clean_towner(tnode_get_clock(node, i));
+
+	if (tnode_get_parent(node))
+		kfree(node);
+
+	return -EINVAL;
+}
+
+/**
+ * tnode_transaction_complete -
+ *
+ * checks the devices status when the transaction is complete.
+ */
+static void tnode_transaction_complete(struct clk_tnode *node)
+{
+	struct klist_iter i;
+	struct pdev_clk_info *dev_info;
+	int j;
+
+	pr_debug("tid: %d\n", (int)tnode_get_id(node));
+	tnode_for_each_valid_events(node, j) {
+	klist_iter_init(&tnode_get_clock(node, j)->devices, &i);
+	while ((dev_info = next_dev_info(&i))) {
+		/* update the device state */
+		struct platform_device *dev = dev_info->pdev;
+		switch (dev->clk_state & (DEV_SUSPENDED_ON_TRANSACTION |
+					  DEV_RESUMED_ON_TRANSACTION)) {
+		case 0: /* this device doesn't care on the clock transaction */
+			atomic_clear_mask(DEV_ON_TRANSACTION,
+				(atomic_t *)&dev->clk_state);
+			break;
+
+		case (DEV_SUSPENDED_ON_TRANSACTION |
+			DEV_RESUMED_ON_TRANSACTION):
+			/* this device was suspended and
+			 * resumed therefore no real change
+			 */
+			pr_debug("dev: %s.%d "
+				"Suspended&Resumed (no child event)\n",
+				dev->name, dev->id);
+			atomic_clear_mask(DEV_ON_TRANSACTION |
+					  DEV_SUSPENDED_ON_TRANSACTION |
+					  DEV_RESUMED_ON_TRANSACTION,
+					  (atomic_t *)&dev->clk_state);
+			break;
+		case DEV_SUSPENDED_ON_TRANSACTION:
+			atomic_clear_mask(DEV_ON_TRANSACTION |
+				DEV_SUSPENDED_ON_TRANSACTION,
+				(atomic_t *)&dev->clk_state);
+			pr_debug("dev: %s.%d Suspended\n",
+				dev->name, dev->id);
+			clk_pm_runtime_device(RPM_SUSPENDED, dev);
+			break;
+		case DEV_RESUMED_ON_TRANSACTION:
+			atomic_clear_mask(DEV_ON_TRANSACTION |
+				DEV_RESUMED_ON_TRANSACTION,
+				(atomic_t *)&dev->clk_state);
+			pr_debug("dev: %s.%d Resumed\n",
+				dev->name, dev->id);
+			clk_pm_runtime_device(RPM_ACTIVE, dev);
+			break;
+
+		default:
+			printk(KERN_ERR "%s: device %s,%d clk_flags _not_ valid %u\n",
+				__func__, dev->name, dev->id,
+				(unsigned int)dev->clk_state);
+		}
+	}
+	klist_iter_exit(&i);
+	clk_clean_towner(tnode_get_clock(node, j));
+	}
+	pr_debug("tid: %d exit\n", (int)tnode_get_id(node));
+	return;
+}
+
+/*
+ * Check if the clk is registered
+ */
+#ifdef CLK_SAFE_CODE
+static struct clk *check_clk(struct clk *clk)
+{
+	struct clk *clkp;
+	struct clk *result = NULL;
+	struct klist_iter i;
+
+	pr_debug("\n");
+
+	klist_iter_init(&clk_list, &i);
+	while ((clkp = next_clock(&i)))
+		if (clk == clkp) {
+			result = clk;
+			break;
+		}
+	klist_iter_exit(&i);
+	return result;
+}
+#else
+static inline struct clk *check_clk(struct clk *clk)
+{
+	return clk;
+}
+#endif
+
+enum child_event_e {
+	CHILD_CLOCK_ENABLED = 1,
+	CHILD_CLOCK_DISABLED,
+	CHILD_DEVICE_ENABLED,
+	CHILD_DEVICE_DISABLED,
+};
+
+static int
+clk_notify_child_event(enum child_event_e const code, struct clk *clk)
+{
+	if (!clk)
+		return 0;
+
+	switch (code) {
+	case CHILD_CLOCK_ENABLED:
+		++clk->nr_active_clocks;
+		break;
+	case CHILD_CLOCK_DISABLED:
+		--clk->nr_active_clocks;
+		break;
+	case CHILD_DEVICE_ENABLED:
+		++clk->nr_active_devices;
+		break;
+	case CHILD_DEVICE_DISABLED:
+		--clk->nr_active_devices;
+		break;
+	}
+
+	if (clk_is_auto_switching(clk)) {
+		/*
+		 * Check if there are still users
+		 */
+		if (!clk->nr_active_devices && !clk->nr_active_clocks)
+			clk_disable(clk);
+		else if (!clk_get_rate(clk)) /* if off.. turn-on */
+			clk_enable(clk);
+	}
+
+	return 0;
+}
+
+/**
+ * clk_dev_events_malloc -
+ *
+ * builds a struct clk_event array (dev_event).
+ * the array size (how many elements) is based on device_num_clocks(dev)
+ * the contenets of each element is equal to:
+ * - the events array (if the idx-clock is under transaction)
+ * - the current clock setting if the idx-clock isn't under transaction
+ */
+static struct clk_event * __must_check
+clk_dev_events_malloc(struct platform_device const *dev)
+{
+	struct clk_event *dev_events;
+	struct clk_tnode *node;
+	int i, j;
+	pr_debug("\n");
+/*
+ * 1.  simple case:
+ *	- device_num_clocks(dev) = 1
+ */
+	if (pdevice_num_clocks(dev) == 1) {
+		node = (struct clk_tnode *)pdevice_clock(dev, 0)->towner;
+		for (i = 0; i < tnode_get_size(node); ++i)
+			if (tnode_get_clock(node, i) == pdevice_clock(dev, 0))
+				return tnode_get_event(node, i);
+	}
+/*
+ * 2. - device_num_clocks(dev) > 1
+ *	GCF has to build a dedicated device events (devents) array
+ *	for this device! sorted as the device registered it-self!
+ */
+	dev_events = kmalloc(sizeof(*dev_events) * pdevice_num_clocks(dev),
+			GFP_KERNEL);
+	if (!dev_events)
+		return NULL;
+
+	for (i = 0; i < pdevice_num_clocks(dev); ++i) {
+		node = (struct clk_tnode *)pdevice_clock(dev, i)->towner;
+		dev_events[i].clk = pdevice_clock(dev, i);
+		if (!node) {/* this means this clocs isn't under transaction */
+		     dev_events[i].old_rate =
+				clk_get_rate(pdevice_clock(dev, i));
+		     dev_events[i].new_rate =
+				clk_get_rate(pdevice_clock(dev, i));
+		     continue;
+		}
+		/* search the right clk_event */
+		for (j = 0; tnode_get_clock(node, j) != pdevice_clock(dev, i);
+		     ++j);
+
+		dev_events[i].old_rate = tnode_get_event(node, j)->old_rate;
+		dev_events[i].new_rate = tnode_get_event(node, j)->new_rate;
+	}
+	return dev_events;
+}
+
+/**
+ * clk_devents_free -
+ * free the devent allocated on the device dev.
+ */
+static inline void
+clk_dev_events_free(struct clk_event *dev_events, struct platform_device *dev)
+{
+	if (pdevice_num_clocks(dev) == 1)
+		return ;
+	kfree(dev_events);
+}
+
+/**
+ * clk_trnsc_fsm -
+ *
+ * propagate the transaction to all the childs
+ * each transaction has the following life-time:
+ *
+ *	+---------------+
+ *	|    ENTER_CLK	|   The ENTER state only for clocks
+ *	+---------------+     - acquires all the clock of the transaction
+ *		|	       - builds the transaction graph
+ *		|	      - for each clock generates a child transaction
+ *		|
+ *   +---------------------+
+ *   |	+---------------+  |
+ *   |	|    ENTER_DEV 	|  |  The ENTER state only for devices
+ *   |  +---------------+  |  - >> NOTIFY_CLK_ENTERCHANGE << notified
+ *   |		|	   |  - - the device could refuse the operation
+ *   |		|	   |
+ *   |	+---------------+  |
+ *   |	|    PRE_DEV	|  |  The PRE state only devices
+ *   |	+---------------+  |  - >> NOTIFY_CLK_PRECHANGE << notified
+ *   |		|	   |  - - the device could be suspended
+ *   +---------------------+
+ *		|
+ *	+---------------+
+ * 	|   CHANGE_CLK	|    The CHANGE state only for clocks
+ *	+---------------+     - updates all the physical clocks
+ *		|	        and relative clk_event_s according to
+ *		|	        the hw value.
+ *   +---------------------+
+ *   |		|	   |
+ *   |	+---------------+  |
+ *   |	|   POST_DEV	|  |  The POST state only for devices
+ *   |  +---------------+  |  - >> NOTIFY_CLK_POSTCHANGE << notified
+ *   |		|	   |  - - the devices could be resumed
+ *   |		|	   |
+ *   |	+---------------+  |
+ *   |	|  EXIT_DEV	|  |   The EXIT state only for devices
+ *   |  +---------------+  |   - >> NOTIFY_CLK_EXITCHANGE << notified
+ *   |		|	   |   - - the devices is aware all the other
+ *   +---------------------+	   devices are resumed.
+ *		|
+ *	+---------------+
+ *	|  EXIT_CLK	|      The EXIT state only for clocks
+ *	+---------------+      (to free all the memory)
+ *				- Free all the allocated memory
+ *
+ */
+
+static enum notify_ret_e
+clk_trnsc_fsm(enum clk_fsm_e const code, struct clk_tnode *node)
+{
+	struct pdev_clk_info *dev_info;
+	struct clk_tnode *tchild;
+	struct klist_iter i;
+	int j;
+	enum notify_ret_e tmp, ret_notifier = NOTIFY_EVENT_HANDLED;
+
+#ifdef CONFIG_CLK_DEBUG
+	switch (code) {
+	case TRNSC_ENTER_CLOCK:
+	case TRNSC_ENTER_DEVICE:
+		printk(KERN_INFO "ENTER_%s ",
+			(code == TRNSC_ENTER_CLOCK ? "CLK" : "DEV"));
+		break;
+	case TRNSC_PRE_DEVICE:
+		printk(KERN_INFO "PRE_DEV ");
+		break;
+	case TRNSC_CHANGE_CLOCK:
+		printk(KERN_INFO "CHANGE_CLK ");
+		break;
+	case TRNSC_POST_DEVICE:
+		printk(KERN_INFO "POST_DEV ");
+		break;
+	case TRNSC_EXIT_DEVICE:
+	case TRNSC_EXIT_CLOCK:
+		printk(KERN_INFO "EXIT_%s ",
+			(code == TRNSC_EXIT_DEVICE ? "DEV" : "CLK"));
+			break;
+	}
+	printk(KERN_INFO"tid:%u ", (unsigned int)tnode_get_id(node));
+	if (tnode_get_parent(node))
+		printk(KERN_INFO " (tpid: %d)",
+			(int)tnode_get_id(tnode_get_parent(node)));
+	printk(KERN_INFO " (0x%x/0x%x) ", (unsigned int)tnode_get_size(node),
+			(unsigned int)tnode_get_map(node));
+	for (j = 0; j < tnode_get_size(node); ++j) {
+		if (tnode_check_map_id(node, j))
+			/* print only the valid event... */
+			printk(KERN_INFO"- %s ",
+				tnode_get_clock(node, j)->name);
+		else if (code == TRNSC_ENTER_CLOCK)
+			printk(KERN_INFO"- %s ",
+				tnode_get_clock(node, j)->name);
+	}
+	printk(KERN_INFO"\n");
+#endif
+
+	/* 
+	 * Clk ENTER state
+	 */
+	if (code == TRNSC_ENTER_CLOCK) {
+		unsigned long idx;
+		enum clk_event_e sub_code;
+		struct clk *clkp;
+		struct clk_event *sub_event = NULL;
+
+		/* first of all the GCF tries to lock the clock of this tnode
+		 * and links the tnode to its parent (if any)
+		 */
+		switch (tnode_lock_clocks(node)) {
+		case 0:
+			break;
+		case -EINVAL:
+			return NOTIFY_EVENT_NOTHANDLED;
+		case 1:
+			return NOTIFY_EVENT_HANDLED;
+		}
+
+		pr_debug("clocks acquired\n");
+		/* Propagates the events to the sub clks */
+		tnode_for_each_valid_events(node, j) {
+
+		if (!clk_allow_propagation(tnode_get_clock(node, j))) {
+			pr_debug("clk: %s doesn't want propagation\n",
+				tnode_get_clock(node, j)->name);
+			continue;
+		}
+		if (!(tnode_get_clock(node, j)->nr_clocks))
+			continue;
+
+		tchild = tnode_malloc(node,
+			tnode_get_clock(node, j)->nr_clocks);
+		if (!tchild) {
+			printk(KERN_ERR "No enough memory during a clk "
+					"transaction\n");
+			ret_notifier |= NOTIFY_EVENT_NOTHANDLED;;
+			return ret_notifier;
+		}
+
+		pr_debug("memory for child transaction acquired\n");
+		idx = 0;
+		sub_code = clk_event_decode(tnode_get_event(node, j));
+		klist_iter_init(&tnode_get_clock(node, j)->childs, &i);
+		while ((clkp = next_child_clock(&i))) {
+			sub_event = tnode_get_event(tchild, idx);
+			clk_event_init(sub_event, clkp, clk_get_rate(clkp),
+				clk_get_rate(clkp));
+			switch (sub_code) {/* prepare the sub event fields */
+			case _CLK_CHANGE:
+			case _CLK_ENABLE:
+				sub_event->new_rate = clk_evaluate_rate(clkp,
+					tnode_get_event(node, j)->new_rate);
+				break;
+			case _CLK_DISABLE:
+				sub_event->new_rate = 0;
+				break;
+			case _CLK_NOCHANGE:
+				break;
+			}
+			++idx;
+			}
+		klist_iter_exit(&i);
+		/* now GCF can araiese the sub transaction */
+		ret_notifier |=
+			clk_trnsc_fsm(code, tchild);
+		}
+		return ret_notifier;
+	}
+
+	/*
+	 * Clk CHANGE state
+	 */
+	if (code == TRNSC_CHANGE_CLOCK) {
+		/* the clocks on the root node are managed directly in the
+		 * clk_set_rate/clk_enable/... functions ...
+		 * while all the other clocks have to managed here!
+		 */
+		if (node->parent)
+			tnode_for_each_valid_events(node, j) {
+				struct clk_event *event;
+				long code;
+				event = tnode_get_event(node, j);
+				code = clk_event_decode(event);
+				switch (code) {
+				case _CLK_CHANGE:
+					__clk_recalc_rate(event->clk);
+					event->new_rate =
+						clk_get_rate(event->clk);
+					break;
+				case _CLK_ENABLE:
+					if (clk_follow_parent(event->clk)) {
+						__clk_enable(event->clk);
+						event->new_rate =
+						clk_get_rate(event->clk);
+					}
+					break;
+				case _CLK_DISABLE:
+					if (clk_is_enabled(event->clk))
+						__clk_disable(event->clk);
+					break;
+				}
+			}
+
+		list_for_each_entry(tchild, &node->childs, pnode)
+			ret_notifier |= clk_trnsc_fsm(code, tchild);
+
+		return ret_notifier;
+	}
+
+	/*
+	 * Clk EXIT state
+	 */
+	if (code == TRNSC_EXIT_CLOCK) {
+		struct list_head *ptr, *next;
+		/* scans all the transaction childs */
+		list_for_each_safe(ptr, next, &node->childs)
+			clk_trnsc_fsm(code, to_tnode(ptr));
+
+		/* update the devices/clocks state */
+		tnode_transaction_complete(node);
+
+		tnode_free(node);
+		pr_debug("EXIT_CLK complete\n");
+
+		return ret_notifier;
+	}
+
+	/*
+	 * Here the devices management
+	 */
+	tnode_for_each_valid_events(node, j) {
+		if (!clk_allow_propagation(tnode_get_clock(node, j)))
+			continue;
+	klist_iter_init(&tnode_get_clock(node, j)->devices, &i);
+	while ((dev_info = next_dev_info(&i))) {
+		struct platform_device *pdev = dev_info->pdev;
+		struct platform_driver *pdrv = 	container_of(
+			pdev->dev.driver, struct platform_driver, driver);
+
+		struct clk_event *dev_events;
+
+		if (!pdrv || !pdrv->notify) {
+			pr_debug(
+			"device %s.%d registered with no notify function\n",
+				pdev->name, pdev->id);
+			continue;
+		}
+		/* check if it already had a 'code' event */
+		if (pdev_transaction_move_on(pdev, code))
+			continue;
+
+		dev_events = clk_dev_events_malloc(pdev);
+		if (!dev_events) {
+			printk(KERN_ERR"%s: No Memory during a clk "
+				"transaction\n", __func__);
+			continue;
+		}
+
+		/* GCF can use 'code' directly in the .notify function
+		 * just because external 'NOTIFY_CLK_xxxCHANGE' code
+		 * matchs with the internal 'device' code
+		 */
+		tmp = pdrv->notify(code, pdev, dev_events);
+		clk_dev_events_free(dev_events, pdev);
+		ret_notifier |= tmp;
+#ifdef CONFIG_PM_RUNTIME
+		if (code == TRNSC_PRE_DEVICE && tmp == NOTIFY_EVENT_HANDLED) {
+			printk(KERN_INFO "clk %s on code %u suspends "
+				"device %s.%d\n",
+				transaction_get_clock(node, j)->name,
+				(unsigned int)code, pdev->name, pdev->id);
+			pm_runtime_suspend(&pdev->dev);
+		} else
+		if (code == TRNSC_POST_DEVICE && tmp == NOTIFY_EVENT_HANDLED) {
+			printk(KERN_INFO "clk %s on code %u resumes "
+				"device %s.%d\n",
+				transaction_get_clock(node, j)->name,
+				(unsigned int)code, pdev->name, pdev->id);
+			pm_runtime_resume(&pdev->dev);
+		};
+#endif
+	} /* while closed */
+	klist_iter_exit(&i);
+	} /* for closed */
+
+	/*
+	 *and propagate down...
+	 */
+	list_for_each_entry(tchild, &node->childs, pnode)
+			ret_notifier |= clk_trnsc_fsm(code, tchild);
+
+	return ret_notifier;
+}
+
+static void clk_initialize(struct clk *clk)
+{
+	kobject_init(&clk->kobj, &ktype_clk);
+	kobject_set_name(&clk->kobj, "%s", clk->name);
+	kobject_get(&clk->kobj);
+
+	clk->nr_clocks = 0;
+	clk->nr_active_clocks = 0;
+	clk->nr_active_devices = 0;
+	clk->towner = NULL;
+
+	klist_init(&clk->childs, klist_get_child, klist_put_child);
+	klist_init(&clk->devices, klist_get_device, klist_put_device);
+
+}
+
+/**
+  * clk_register -
+  *
+  * registers a new clk in the system.
+  * returns zero if success
+  */
+int clk_register(struct clk *clk)
+{
+	int ret = 0;
+	if (!clk)
+		return -EFAULT;
+	pr_debug("%s\n", clk->name);
+
+	clk_initialize(clk);
+
+	/* Initialize ... */
+	__clk_init(clk);
+
+	if (clk->parent) {
+#ifdef CLK_SAFE_CODE
+		/* 1. the parent has to be registered */
+		if (!check_clk(clk->parent))
+			return -ENODEV;
+		/* 2. an always enabled child has to sit on a always
+		 *    enabled parent!
+		 */
+		if (clk->flags & CLK_ALWAYS_ENABLED &&
+			!(clk->parent->flags & CLK_ALWAYS_ENABLED))
+			return -EFAULT;
+		/* 3. a fixed child has to sit on a fixed parent */
+		if (clk_is_readonly(clk) && !clk_is_readonly(clk->parent))
+			return -EFAULT;
+#endif
+		klist_add_tail(&clk->child_node, &clk->parent->childs);
+		clk->parent->nr_clocks++;
+	}
+
+	ret = kobject_add(&clk->kobj,
+		(clk->parent ? &clk->parent->kobj : clk_kobj), clk->name);
+	if (ret)
+		goto err_0;
+
+	clk->kdevices =	kobject_create_and_add("devices", &clk->kobj);
+	if (!clk->kdevices)
+		goto err_1;
+
+	klist_add_tail(&clk->node, &clk_list);
+	if (clk->flags & CLK_ALWAYS_ENABLED) {
+		__clk_enable(clk);
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+	}
+	return ret;
+
+err_1:
+	/* subsystem_remove_file... removed in the common code... ??? */
+	kobject_del(&clk->kobj);
+err_0:
+	return ret;
+}
+EXPORT_SYMBOL(clk_register);
+
+/**
+  * clk_unregister -
+  * unregisters the clock from system
+  */
+int clk_unregister(struct clk *clk)
+{
+	pr_debug("\n");
+
+	if (!clk)
+		return -EFAULT;
+
+	if (!list_empty(&clk->devices.k_list))
+		return -EFAULT; /* somebody is still using this clock */
+
+	kobject_del(clk->kdevices);
+	kfree(clk->kdevices);
+	/* subsystem_remove_file... removed in the common code... ??? */
+	kobject_del(&clk->kobj);
+	klist_del(&clk->node);
+	if (clk->parent) {
+		klist_del(&clk->child_node);
+		clk->parent->nr_clocks--;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(clk_unregister);
+
+static int clk_add_devinfo(struct pdev_clk_info *info)
+{
+	int ret = 0;
+	pr_debug("\n");
+
+#ifdef CLK_SAFE_CODE
+	if (!info || !info->clk || !check_clk(info->clk))
+		return -EFAULT;
+#endif
+	ret = sysfs_create_link(info->clk->kdevices, &info->pdev->dev.kobj,
+		dev_name(&info->pdev->dev));
+	if (ret) {
+		pr_debug(" Error %d\n", ret);
+		return ret;
+	}
+	klist_add_tail(&info->node, &info->clk->devices);
+
+	return 0;
+}
+
+static int clk_del_devinfo(struct pdev_clk_info *info)
+{
+	pr_debug("\n");
+
+#ifdef CLK_SAFE_CODE
+	if (!info || !info->clk || !check_clk(info->clk))
+		return -EFAULT;
+#endif
+	sysfs_remove_link(info->clk->kdevices, dev_name(&info->pdev->dev));
+	klist_del(&info->node);
+
+#ifndef CONFIG_PM_RUNTIME
+	/*
+	 * Without PM_RUNTIME the GCF assumes the device is
+	 * 'not active' when it's removed
+	 */
+	clk_notify_child_event(CHILD_DEVICE_DISABLED, info->clk);
+#endif
+	return 0;
+}
+
+int clk_probe_device(struct platform_device *dev, enum pdev_probe_state state)
+{
+	int idx;
+	switch (state) {
+	case PDEV_PROBEING:
+		/* before the .probe function is called the GCF
+		 * has to turn-on _all_ the clocks the device uses
+		 * to garantee a safe .probe
+		 */
+		for (idx = 0; idx < pdevice_num_clocks(dev); ++idx)
+			if (pdevice_clock(dev, idx))
+				clk_enable(pdevice_clock(dev, idx));
+		return 0;
+	case PDEV_PROBED:
+#ifdef CONFIG_PM_RUNTIME
+	/*
+	 * Here the GCF should check the device's pm_runtime state
+	 * And if the device is suspended the clk_frmwk can turn-off the clocks
+	 */
+#else
+	/*
+	 * Without PM_RUNTIME the GCF assumes the device is active
+	 */
+	for (idx = 0; idx < pdevice_num_clocks(dev); ++idx)
+		clk_notify_child_event(CHILD_DEVICE_ENABLED,
+			pdevice_clock(dev, idx));
+#endif
+	break;
+	case PDEV_PROBE_FAILED:
+	/*
+	 * TO DO something...
+	 */
+		break;
+	}
+	return 0;
+}
+
+int clk_add_device(struct platform_device *dev, enum pdev_add_state state)
+{
+	int idx;
+	int ret;
+
+	if (!dev)
+		return -EFAULT;
+
+	switch (state) {
+	case PDEV_ADDING:
+	case PDEV_ADD_FAILED:
+		/*
+		 * TO DO something
+		 */
+		return 0;
+	case PDEV_ADDED:
+		break;
+	}
+	/* case PDEV_ADDED ... */
+	if (!dev->clks || !pdevice_num_clocks(dev))
+		return 0;	/* this device will not use
+				   the clk framework */
+
+	pr_debug("%s.%d with %u clocks\n", dev->name, dev->id,
+		(unsigned int)pdevice_num_clocks(dev));
+
+	dev->clk_state = 0;
+	for (idx = 0; idx < pdevice_num_clocks(dev); ++idx) {
+		if (!pdevice_clock(dev, idx)) {	/* clk can not be NULL... */
+			pr_debug("Error clock NULL\n");
+			continue;
+		}
+		pr_debug("->under %s\n", dev->clks[idx].clk->name);
+		dev->clks[idx].pdev = dev;
+		ret = clk_add_devinfo(&dev->clks[idx]);
+		if (ret)
+			goto err_0;
+	}
+
+	return 0;
+err_0:
+	for (--idx; idx >= 0; --idx)
+		clk_del_devinfo(&dev->clks[idx]);
+
+	return -EINVAL;
+}
+
+int clk_del_device(struct platform_device *dev)
+{
+	int idx;
+	if (!dev)
+		return -EFAULT;
+
+	for (idx = 0; idx < pdevice_num_clocks(dev); ++idx)
+		clk_del_devinfo(&dev->clks[idx]);
+
+	return 0;
+}
+
+void clk_put(struct clk *clk)
+{
+	if (clk && !IS_ERR(clk))
+		kobject_put(&clk->kobj);
+}
+
+static int clk_is_parent(struct clk const *child, struct clk const *parent)
+{
+	if (!child || !parent)
+		return 0;
+	if (!child->parent)
+		return 0;
+	if (child->parent == parent)
+		return 1;
+	else
+		return clk_is_parent(child->parent, parent);
+}
+
+int clk_enable(struct clk *clk)
+{
+	int ret;
+	struct clk_tnode transaction;
+	struct clk_event event;
+
+	event = EVENT(clk, 0, CLK_UNDEFINED_RATE);
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	pr_debug("%s\n", clk->name);
+
+
+	if (clk->flags & CLK_ALWAYS_ENABLED || clk_is_enabled(clk))
+		return 0;
+
+	if (clk->parent) {
+		/* turn-on the parent if the parent is 'auto_switch' */
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+
+		if (!clk_is_enabled(clk->parent)) {
+			/* the parent is still disabled... */
+			clk_notify_child_event(CHILD_CLOCK_DISABLED,
+				clk->parent);
+			return -EINVAL;
+		}
+	}
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock update */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+		}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	ret = __clk_enable(clk);
+
+	event.new_rate = clk_get_rate(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	if (ret)
+		clk_notify_child_event(CHILD_CLOCK_DISABLED, clk->parent);
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+/**
+ * clk_disable -
+ * disables the clock
+ * Is isn't really good that it's a 'void' function...
+ * but this is common interface
+ */
+void clk_disable(struct clk *clk)
+{
+	struct clk_tnode transaction;
+	struct clk_event event;
+	int ret;
+
+	event = EVENT(clk, clk_get_rate(clk), 0);
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	pr_debug("\n");
+
+	if (clk->flags & CLK_ALWAYS_ENABLED || !clk_is_enabled(clk))
+		return;
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret)
+		goto err_0;
+
+	/* if not zero somebody doens't agree the clock update */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret)
+		goto err_1;
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	__clk_disable(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	clk_notify_child_event(CHILD_CLOCK_DISABLED, clk->parent);
+
+	return ;
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	int ret = -EOPNOTSUPP;
+	struct clk *old_parent = clk->parent;
+	struct clk_event event;
+	struct clk_tnode transaction;
+	int clk_was_enabled = clk_is_enabled(clk);
+
+	event = EVENT(clk, clk_get_rate(clk), CLK_UNDEFINED_RATE);
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	if (!clk || !parent)
+		return -EINVAL;
+
+	if (clk->parent == parent)
+		return 0;
+
+	pr_debug("\n");
+
+	if (clk_was_enabled && !clk_is_enabled(parent))
+		/* turn-on parent if possible */
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, parent);
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock updated */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+	}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	/* Now we updated the hw */
+	ret = __clk_set_parent(clk, parent);
+	if (ret) {
+		/* there was a problem...
+		 * therefore clk is still on the old parent
+		 */
+		clk->parent = old_parent; /* to be safe ! */
+		goto err_2;
+	}
+
+	klist_del(&clk->child_node);
+
+	clk->parent = parent;
+
+	ret = kobject_move(&clk->kobj, &clk->parent->kobj);
+	if (ret)
+		;
+
+	klist_add_tail(&clk->child_node, &clk->parent->childs);
+
+	clk->parent->nr_clocks++;
+	old_parent->nr_clocks--;
+
+err_2:
+	event.new_rate = clk_get_rate(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	if (clk_was_enabled && !ret) {
+		/* 5. to decrease the old_parent nchild counter */
+		clk_notify_child_event(CHILD_CLOCK_DISABLED, old_parent);
+		/* 5. increase the new_parent nchild counter */
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+		/* 6. to decrease the old_parent nchild counter */
+		clk_notify_child_event(CHILD_CLOCK_DISABLED, old_parent);
+		}
+
+	return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret = -EOPNOTSUPP;
+	struct clk_event event;
+	struct clk_tnode transaction;
+
+	event = EVENT(clk, clk_get_rate(clk), clk_round_rate(clk, rate));
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	pr_debug("\n");
+
+	if (clk_is_readonly(clk))
+		/* read only clock doesn't have to be "touched" !!!! */
+		return -EPERM;
+
+	if (event.new_rate == clk_get_rate(clk))
+		return 0;
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock updated */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+	}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	__clk_set_rate(clk, event.new_rate);
+	/* reload new_rate to avoid hw rounding... */
+	event.new_rate = clk_get_rate(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	pr_debug("\n");
+
+	if (likely(clk->ops && clk->ops->round))
+		return clk->ops->round(clk, rate);
+	return rate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+unsigned long clk_evaluate_rate(struct clk *clk, unsigned long prate)
+{
+	pr_debug("\n");
+	if (!clk->parent)/* without parent this function has no meaning */
+		return CLK_UNDEFINED_RATE;
+
+	if (!prate)/* on parent disabled than disable the child */
+		return 0;
+
+	if (likely(clk->ops && clk->ops->eval))
+		return clk->ops->eval(clk, prate);
+
+	return CLK_UNDEFINED_RATE;
+}
+EXPORT_SYMBOL(clk_evaluate_rate);
+
+int clk_set_rates(struct clk **clks, unsigned long *rates, unsigned long nclks)
+{
+	int i, ret = 0;
+	struct clk_event *evt;
+	struct clk_tnode transaction = TRANSACTION_ROOT(nclks, NULL)
+
+	pr_debug("\n");
+
+	if (!clks || !rates || !nclks)
+		return -EINVAL;
+	evt = kmalloc(sizeof(*evt) *
+		tnode_get_size(&transaction), GFP_KERNEL);
+
+	if (!evt)
+		return -ENOMEM;
+
+	tnode_set_events(&transaction, evt);
+
+	for (i = 0; i < tnode_get_size(&transaction); ++i) {
+		tnode_set_clock(&transaction, i, clks[i]);
+		tnode_get_event(&transaction, i)->old_rate =
+			clk_get_rate(clks[i]);
+		tnode_get_event(&transaction, i)->new_rate =
+			clk_round_rate(clks[i], rates[i]);
+	}
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock updated */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+	}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	for (i = 0; i < tnode_get_size(&transaction); ++i) {
+		if (!clk_is_enabled(clks[i]) && rates[i])
+			ret |= __clk_enable(clks[i]);
+		else if (clk_is_enabled(clks[i]) && !rates[i])
+			ret |= __clk_disable(clks[i]);
+		else
+			ret |= __clk_set_rate(clks[i], rates[i]);
+
+		/* reload new_rate to avoid hw rounding... */
+		tnode_get_event(&transaction, i)->new_rate =
+			clk_get_rate(clks[i]);
+	}
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	kfree(evt);
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rates);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	struct clk *clk = NULL;
+	struct clk *clkp;
+	struct klist_iter i;
+	int found = 0, idno;
+
+	mutex_lock(&clk_list_sem);
+#if 0
+	if (dev == NULL || dev->bus != &platform_bus_type)
+		idno = -1;
+	else
+		idno = to_platform_device(dev)->id;
+
+	klist_iter_init(&clk_list, &i);
+	while ((clkp = next_clock(&i)) && !found)
+		if (clk->id == idno && strcmp(id, clk->name) == 0 &&
+			try_module_get(clk->owner)) {
+				clk = clkp;
+				found = 1;
+		}
+	klist_iter_exit(&i);
+
+	if (found)
+		goto _found;
+#endif
+	klist_iter_init(&clk_list, &i);
+	while ((clkp = next_clock(&i)))
+		if (strcmp(id, clkp->name) == 0
+		    && try_module_get(clkp->owner)) {
+			clk = clkp;
+			break;
+		}
+	klist_iter_exit(&i);
+_found:
+	mutex_unlock(&clk_list_sem);
+	return clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+int clk_for_each(int (*fn) (struct clk *clk, void *data), void *data)
+{
+	struct clk *clkp;
+	struct klist_iter i;
+	int result = 0;
+
+	if (!fn)
+		return -EFAULT;
+
+	pr_debug("\n");
+	mutex_lock(&clk_list_sem);
+	klist_iter_init(&clk_list, &i);
+
+	while ((clkp = next_clock(&i)))
+		result |= fn(clkp, data);
+
+	klist_iter_exit(&i);
+	mutex_unlock(&clk_list_sem);
+	return result;
+}
+EXPORT_SYMBOL(clk_for_each);
+
+int clk_for_each_child(struct clk *clk,
+	int (*fn) (struct clk *clk, void *data), void *data)
+{
+	struct clk *clkp;
+	struct klist_iter i;
+	int result = 0;
+
+	if (!clk || !fn)
+		return -EFAULT;
+
+	klist_iter_init(&clk->childs, &i);
+
+	while ((clkp = next_child_clock(&i)))
+		result |= fn(clkp, data);
+
+	klist_iter_exit(&i);
+
+	return result;
+}
+EXPORT_SYMBOL(clk_for_each_child);
+
+static int __init early_clk_complete(struct clk *clk, void *data)
+{
+	int ret;
+
+	ret = kobject_add(&clk->kobj,
+		(clk->parent ? &clk->parent->kobj : clk_kobj),
+		clk->name);
+	if (ret)
+		return ret;
+
+	clk->kdevices = kobject_create_and_add("devices", &clk->kobj);
+	if (!clk->kdevices)
+		return -EINVAL;
+
+	return 0;
+}
+
+int __init early_clk_register(struct clk *clk)
+{
+	int retval = 0;
+	if (!clk)
+		return -EFAULT;
+	pr_debug("%s\n", clk->name);
+
+	clk_initialize(clk);
+
+	/* Initialize ... */
+	__clk_init(clk);
+
+	if (clk->parent) {
+#ifdef CLK_SAFE_CODE
+		/* 1. the parent has to be registered */
+		if (!check_clk(clk->parent))
+			return -ENODEV;
+		/* 2. an always enabled child has to sit on a always
+		 *    enabled parent!
+		 */
+		if (clk->flags & CLK_ALWAYS_ENABLED &&
+			!(clk->parent->flags & CLK_ALWAYS_ENABLED))
+			return -EFAULT;
+		/* 3. a fixed child has to sit on a fixed parent */
+		if (clk_is_readonly(clk) && !clk_is_readonly(clk->parent))
+			return -EFAULT;
+#endif
+		klist_add_tail(&clk->child_node, &clk->parent->childs);
+		clk->parent->nr_clocks++;
+	}
+
+	klist_add_tail(&clk->node, &clk_list);
+	if (clk->flags & CLK_ALWAYS_ENABLED) {
+		__clk_enable(clk);
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+	}
+	return retval;
+}
+
+int __init clock_init(void)
+{
+	clk_kobj = kobject_create_and_add("clocks", NULL);
+	if (!clk_kobj)
+		return -EINVAL ;
+
+	clk_for_each(early_clk_complete, NULL);
+
+	printk(KERN_INFO CLK_NAME " " CLK_VERSION "\n");
+
+	return 0;
+}
+
diff --git a/drivers/base/clk.h b/drivers/base/clk.h
new file mode 100644
index 0000000..61672ef
--- /dev/null
+++ b/drivers/base/clk.h
@@ -0,0 +1,319 @@
+/*
+   -------------------------------------------------------------------------
+   clk.h
+   -------------------------------------------------------------------------
+   (C) STMicroelectronics 2008
+   (C) STMicroelectronics 2009
+   Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+   ----------------------------------------------------------------------------
+   May be copied or modified under the terms of the GNU General Public
+   License v.2 ONLY.  See linux/COPYING for more information.
+
+   ------------------------------------------------------------------------- */
+
+#ifdef CONFIG_GENERIC_CLK_FM
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/klist.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <asm/atomic.h>
+
+enum clk_ops_id {
+	__CLK_INIT = 0,
+	__CLK_ENABLE,
+	__CLK_DISABLE,
+	__CLK_SET_RATE,
+	__CLK_SET_PARENT,
+	__CLK_RECALC,
+	__CLK_ROUND,
+	__CLK_EVAL,
+};
+
+extern struct klist clk_list;
+/**
+  * clk_tnode
+  *      it's the internal strucure used to track each node
+  *      in the transaction graph.
+  *      _NO_ api is showed to the other modules
+  */
+struct clk_tnode {
+	/** @tid: the tnode id */
+	unsigned long tid;
+	/** @size: how may clock are involved in this tnode */
+	unsigned long size;
+	/** @parent: the parent tnode */
+	struct clk_tnode *parent;
+	/* @events_map: a bitmap to declare the
+	 * valid events in this tnode
+	 */
+	unsigned long events_map;
+	/** @events: the event array of this tnode */
+	struct clk_event *events;
+	/** @child: links the childres tnode */
+	struct list_head childs;
+	/** @pnode: links the tnode to the parent */
+	struct list_head pnode;
+};
+
+/*
+ *  tnode_get_size -
+ *  returns the number of events in the transaction
+ */
+static inline unsigned long
+tnode_get_size(struct clk_tnode *tnode)
+{
+	return tnode->size;
+}
+
+static inline unsigned long
+tnode_get_map(struct clk_tnode *tnode)
+{
+	return tnode->events_map;
+}
+
+static inline unsigned long
+tnode_check_map_id(struct clk_tnode *node, int id)
+{
+	return node->events_map & (1 << id);
+}
+
+static inline void
+tnode_set_map_id(struct clk_tnode *node, int id)
+{
+	node->events_map |= (1 << id);
+}
+
+static inline unsigned long
+tnode_get_id(struct clk_tnode *node)
+{
+	return node->tid;
+}
+
+static inline struct clk_event*
+tnode_get_event(struct clk_tnode *node, int id)
+{
+	return &(node->events[id]);
+}
+
+static inline struct clk_event *tnode_get_events(struct clk_tnode *node)
+{
+	return tnode_get_event(node, 0);
+}
+
+static inline void
+tnode_set_events(struct clk_tnode *node, struct clk_event *events)
+{
+	node->events = events;
+}
+
+static inline struct clk*
+tnode_get_clock(struct clk_tnode *node, int id)
+{
+	return tnode_get_event(node, id)->clk;
+}
+
+static inline void
+tnode_set_clock(struct clk_tnode *node, int id, struct clk *clk)
+{
+	node->events[id].clk = clk;
+}
+
+static inline struct clk_tnode *tnode_get_parent(struct clk_tnode *node)
+{
+	return node->parent;
+}
+
+#define tnode_for_each_valid_events(node, _j)			\
+	for ((_j) = (ffs(tnode_get_map(node)) - 1);		\
+	     (_j) < tnode_get_size((node)); ++(_j))		\
+			if (tnode_check_map_id((node), (_j)))
+
+#define EVENT(_clk,  _oldrate, _newrate)		\
+	(struct clk_event)				\
+	{						\
+		.clk = (struct clk *)(_clk),		\
+		.old_rate = (unsigned long)(_oldrate),	\
+		.new_rate = (unsigned long)(_newrate),	\
+	};
+
+#define TRANSACTION_ROOT(_num, _event)					\
+	(struct clk_tnode) {						\
+		.tid    = atomic_inc_return(&transaction_counter),	\
+		.size   = (_num),					\
+		.events = (struct clk_event *)(_event),			\
+		.parent = NULL,						\
+		.childs = LIST_HEAD_INIT(transaction.childs),		\
+		.events_map = 0,					\
+		};
+
+#define klist_function_support(_name, _type, _field, _kobj)		\
+static void klist_get_##_name(struct klist_node *n)			\
+{									\
+	struct _type *entry = container_of(n, struct _type, _field);	\
+	kobject_get(&entry->_kobj);					\
+}									\
+static void klist_put_##_name(struct klist_node *n)			\
+{									\
+	struct _type *entry = container_of(n, struct _type, _field);	\
+	kobject_put(&entry->_kobj);					\
+}
+
+#define klist_entry_support(name, type, field)				\
+static struct type *next_##name(struct klist_iter *i)			\
+{	struct klist_node *n = klist_next(i);				\
+	return n ? container_of(n, struct type, field) : NULL;		\
+}
+
+static inline void
+clk_event_init(struct clk_event *evt, struct clk *clk,
+		unsigned long oldrate, unsigned long newrate)
+{
+	evt->clk      = clk;
+	evt->old_rate = oldrate;
+	evt->new_rate = newrate;
+}
+
+enum clk_fsm_e {
+	TRNSC_ENTER_CLOCK	= 0x10,
+	TRNSC_ENTER_DEVICE	= NOTIFY_CLK_ENTERCHANGE,	/* 0x1 */
+	TRNSC_PRE_DEVICE	= NOTIFY_CLK_PRECHANGE,		/* 0x2 */
+	TRNSC_CHANGE_CLOCK	= 0x20,
+	TRNSC_POST_DEVICE	= NOTIFY_CLK_POSTCHANGE,	/* 0x4 */
+	TRNSC_EXIT_DEVICE	= NOTIFY_CLK_EXITCHANGE,	/* 0x8 */
+	TRNSC_EXIT_CLOCK	= 0x40
+};
+
+#define DEV_SUSPENDED_ON_TRANSACTION	(0x10)
+#define DEV_RESUMED_ON_TRANSACTION	(0x20)
+#define DEV_ON_TRANSACTION	(TRNSC_ENTER_DEVICE	|	\
+				TRNSC_PRE_DEVICE	|	\
+				TRNSC_POST_DEVICE	|	\
+				TRNSC_EXIT_DEVICE)
+
+static inline int
+pdev_transaction_move_on(struct platform_device *dev, unsigned int value)
+{
+	int ret = -EINVAL;
+	unsigned long flag;
+#ifdef CONFIG_CLK_DEBUG
+	static const char *dev_state[] = {
+		"dev_enter",
+		"dev_pre",
+		"dev_post",
+		"dev_exit"
+	};
+
+	unsigned long old = dev->clk_state & DEV_ON_TRANSACTION;
+	int was = 0, is = 0;
+	if (
+	   (old == 0 && value == TRNSC_ENTER_DEVICE) ||
+	   (old == TRNSC_ENTER_DEVICE && value == TRNSC_EXIT_DEVICE) ||
+	   (old == TRNSC_ENTER_DEVICE && value == TRNSC_PRE_DEVICE) ||
+	   (old == TRNSC_PRE_DEVICE && value == TRNSC_POST_DEVICE) ||
+	   (old == TRNSC_POST_DEVICE && value == TRNSC_EXIT_DEVICE))
+		goto ok;
+	switch (old) {
+	case TRNSC_ENTER_DEVICE:
+		was = 0;
+		break;
+	case TRNSC_PRE_DEVICE:
+		was = 1;
+		break;
+	case TRNSC_POST_DEVICE:
+		was = 2;
+		break;
+	case TRNSC_EXIT_DEVICE:
+		was = 3;
+		break;
+	}
+	switch (value) {
+	case TRNSC_ENTER_DEVICE:
+		is = 0;
+		break;
+	case TRNSC_PRE_DEVICE:
+		is = 1;
+		break;
+	case TRNSC_POST_DEVICE:
+		is = 2;
+		break;
+	case TRNSC_EXIT_DEVICE:
+		is = 3;
+		break;
+	}
+	printk(KERN_ERR "The device %s.%d shows a wrong evolution during "
+		"a clock transaction\nDev state was %s and moved on %s\n",
+		dev->name, dev->id, dev_state[was], dev_state[is]);
+ok:
+#endif
+	local_irq_save(flag);
+	if ((dev->clk_state & DEV_ON_TRANSACTION) != value) {
+		dev->clk_state &= ~DEV_ON_TRANSACTION;
+		dev->clk_state |= value;
+		ret = 0;
+	}
+	local_irq_restore(flag);
+	return ret;
+}
+
+static inline int
+clk_set_towner(struct clk *clk, struct clk_tnode *node)
+{
+	return atomic_cmpxchg((atomic_t *)&clk->towner, 0, (int)node);
+}
+
+static inline void
+clk_clean_towner(struct clk *clk)
+{
+	atomic_set((atomic_t *)(&clk->towner), 0);
+}
+
+static inline int
+clk_is_enabled(struct clk *clk)
+{
+	return clk->rate != 0;
+}
+
+static inline int
+clk_is_readonly(struct clk *clk)
+{
+	return !clk->ops || !clk->ops->set_rate;
+}
+
+static inline int
+clk_allow_propagation(struct clk *clk)
+{
+	return !!(clk->flags & CLK_EVENT_PROPAGATES);
+}
+
+static inline int
+clk_is_auto_switching(struct clk *clk)
+{
+	return !!(clk->flags & CLK_AUTO_SWITCHING);
+}
+
+static inline int
+clk_follow_parent(struct clk *clk)
+{
+	return !!(clk->flags & CLK_FOLLOW_PARENT);
+}
+
+enum pdev_add_state {
+	PDEV_ADDING,
+	PDEV_ADDED,
+	PDEV_ADD_FAILED,
+};
+
+enum pdev_probe_state {
+	PDEV_PROBEING,
+	PDEV_PROBED,
+	PDEV_PROBE_FAILED,
+};
+
+int clk_add_device(struct platform_device *dev, enum pdev_add_state state);
+int clk_probe_device(struct platform_device *dev, enum pdev_probe_state state);
+int clk_del_device(struct platform_device *dev);
+
+#endif
diff --git a/drivers/base/clk_pm.c b/drivers/base/clk_pm.c
new file mode 100644
index 0000000..56c1760
--- /dev/null
+++ b/drivers/base/clk_pm.c
@@ -0,0 +1,197 @@
+/*
+ * -------------------------------------------------------------------------
+ * clk_pm.c
+ * -------------------------------------------------------------------------
+ * (C) STMicroelectronics 2008
+ * (C) STMicroelectronics 2009
+ * Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+ * -------------------------------------------------------------------------
+ * May be copied or modified under the terms of the GNU General Public
+ * License v.2 ONLY.  See linux/COPYING for more information.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <linux/clk.h>
+#include <linux/klist.h>
+#include <linux/list.h>
+#include <linux/sysdev.h>
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include "power/power.h"
+#include "clk.h"
+#include "base.h"
+
+static int
+__clk_operations(struct clk *clk, unsigned long rate, enum clk_ops_id id_ops)
+{
+	int ret = -EINVAL;
+	unsigned long *ops_fns = (unsigned long *)clk->ops;
+	if (likely(ops_fns && ops_fns[id_ops])) {
+		int (*fns)(struct clk *clk, unsigned long rate)
+			= (void *)ops_fns[id_ops];
+		unsigned long flags;
+		spin_lock_irqsave(&clk->lock, flags);
+		ret = fns(clk, rate);
+		spin_unlock_irqrestore(&clk->lock, flags);
+	}
+	return ret;
+}
+
+static inline int __clk_init(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_INIT);
+}
+
+static inline int __clk_enable(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_ENABLE);
+}
+
+static inline int __clk_disable(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_DISABLE);
+}
+
+static inline int __clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	return __clk_operations(clk, rate, __CLK_SET_RATE);
+}
+
+static inline int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	return __clk_operations(clk, (unsigned long)parent, __CLK_SET_PARENT);
+}
+
+static inline int __clk_recalc_rate(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_RECALC);
+}
+
+static inline int pm_clk_ratio(struct clk *clk)
+{
+	register unsigned int val, exp;
+
+	val = ((clk->flags >> CLK_PM_RATIO_SHIFT) &
+		((1 << CLK_PM_RATIO_NRBITS) - 1)) + 1;
+	exp = ((clk->flags >> CLK_PM_EXP_SHIFT) &
+		((1 << CLK_PM_EXP_NRBITS) - 1));
+
+	return val << exp;
+}
+
+static inline int pm_clk_is_off(struct clk *clk)
+{
+	return ((clk->flags & CLK_PM_TURNOFF) == CLK_PM_TURNOFF);
+}
+
+static inline void pm_clk_set(struct clk *clk, int edited)
+{
+#define CLK_PM_EDITED (1 << CLK_PM_EDIT_SHIFT)
+	clk->flags &= ~CLK_PM_EDITED;
+	clk->flags |= (edited ? CLK_PM_EDITED : 0);
+}
+
+static inline int pm_clk_is_modified(struct clk *clk)
+{
+	return ((clk->flags & CLK_PM_EDITED) != 0);
+}
+
+static int clk_resume_from_standby(struct clk *clk, void *data)
+{
+	pr_debug("\n");
+	if (!likely(clk->ops))
+		return 0;
+	/* check if the pm modified the clock */
+	if (!pm_clk_is_modified(clk))
+		return 0;;
+	pm_clk_set(clk, 0);
+	if (pm_clk_is_off(clk))
+		__clk_enable(clk);
+	else
+		__clk_set_rate(clk, clk->rate * pm_clk_ratio(clk));
+	return 0;
+}
+
+static int clk_on_standby(struct clk *clk, void *data)
+{
+	pr_debug("\n");
+
+	if (!clk->ops)
+		return 0;
+	if (!clk->rate) /* already disabled */
+		return 0;
+
+	pm_clk_set(clk, 1);	/* set as modified */
+	if (pm_clk_is_off(clk))		/* turn-off */
+		__clk_disable(clk);
+	else    /* reduce */
+		__clk_set_rate(clk, clk->rate / pm_clk_ratio(clk));
+	return 0;
+}
+
+static int clk_resume_from_hibernation(struct clk *clk, void *data)
+{
+	unsigned long rate = clk->rate;
+	pr_debug("\n");
+	__clk_set_parent(clk, clk->parent);
+	__clk_set_rate(clk, rate);
+	__clk_recalc_rate(clk);
+	return 0;
+}
+
+static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+{
+	static pm_message_t prev_state;
+
+	switch (state.event) {
+	case PM_EVENT_ON:
+		switch (prev_state.event) {
+		case PM_EVENT_FREEZE: /* Resumeing from hibernation */
+			clk_for_each(clk_resume_from_hibernation, NULL);
+			break;
+		case PM_EVENT_SUSPEND:
+			clk_for_each(clk_resume_from_standby, NULL);
+			break;
+		}
+	case PM_EVENT_SUSPEND:
+		clk_for_each(clk_on_standby, NULL);
+		break;
+	case PM_EVENT_FREEZE:
+		break;
+	}
+	prev_state = state;
+	return 0;
+}
+
+static int clks_sysdev_resume(struct sys_device *dev)
+{
+	return clks_sysdev_suspend(dev, PMSG_ON);
+}
+
+static struct sysdev_class clk_sysdev_class = {
+	.name = "clks",
+};
+
+static struct sysdev_driver clks_sysdev_driver = {
+	.suspend = clks_sysdev_suspend,
+	.resume = clks_sysdev_resume,
+};
+
+static struct sys_device clks_sysdev_dev = {
+	.cls = &clk_sysdev_class,
+};
+
+static int __init clk_sysdev_init(void)
+{
+	sysdev_class_register(&clk_sysdev_class);
+	sysdev_driver_register(&clk_sysdev_class, &clks_sysdev_driver);
+	sysdev_register(&clks_sysdev_dev);
+	return 0;
+}
+
+subsys_initcall(clk_sysdev_init);
diff --git a/drivers/base/clk_utils.c b/drivers/base/clk_utils.c
new file mode 100644
index 0000000..a222aa7
--- /dev/null
+++ b/drivers/base/clk_utils.c
@@ -0,0 +1,456 @@
+/*
+ * -------------------------------------------------------------------------
+ * clk_utils.c
+ * -------------------------------------------------------------------------
+ * (C) STMicroelectronics 2008
+ * (C) STMicroelectronics 2009
+ * Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+ * -------------------------------------------------------------------------
+ * May be copied or modified under the terms of the GNU General Public
+ * License v.2 ONLY.  See linux/COPYING for more information.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/klist.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/sysdev.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include "power/power.h"
+#include "clk.h"
+#include "base.h"
+
+int clk_generic_notify(unsigned long code,
+	struct platform_device *pdev, void *data)
+{
+	struct clk_event *event = (struct clk_event *)data;
+	unsigned long event_decode = clk_event_decode(event);
+
+	switch (code) {
+	case NOTIFY_CLK_ENTERCHANGE:
+		return NOTIFY_EVENT_HANDLED;	/* to accept */
+
+	case NOTIFY_CLK_PRECHANGE:
+		/* without clock (not still enabled) the device can not work */
+		if (event_decode == _CLK_ENABLE)
+			return NOTIFY_EVENT_NOTHANDLED;
+		return NOTIFY_EVENT_HANDLED;	/* to suspend */
+
+	case NOTIFY_CLK_POSTCHANGE:
+		/* without clock (just disabled) the device can not work */
+		if (event_decode == _CLK_DISABLE)
+			return NOTIFY_EVENT_NOTHANDLED;
+		return NOTIFY_EVENT_HANDLED;	/* to resume */
+
+	case NOTIFY_CLK_EXITCHANGE:
+		return NOTIFY_EVENT_HANDLED;
+	}
+
+	return NOTIFY_EVENT_HANDLED;
+}
+EXPORT_SYMBOL(clk_generic_notify);
+
+unsigned long clk_generic_evaluate_rate(struct clk *clk, unsigned long prate)
+{
+	unsigned long current_prate;
+
+	if (!clk->parent)
+		return -EINVAL;
+
+	if (!prate)	/* if zero return zero (on disable: disable!) */
+		return 0;
+
+	if (prate == CLK_UNDEFINED_RATE) /* on undefined: undefined */
+		return CLK_UNDEFINED_RATE;
+
+	current_prate = clk_get_rate(clk->parent);
+	if (current_prate == prate)
+		return clk_get_rate(clk);
+
+	if (current_prate > prate) /* down scale */
+		return (clk_get_rate(clk) * prate) / current_prate;
+	else
+		return (clk_get_rate(clk) / current_prate) * prate;
+}
+EXPORT_SYMBOL(clk_generic_evaluate_rate);
+
+#ifdef CONFIG_PROC_FS
+/*
+ * The "clocks" file is created under /proc
+ * to list all the clocks registered in the system
+ */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+static void *clk_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	struct list_head *tmp;
+	union {
+		loff_t value;
+		long parts[2];
+	} ltmp;
+
+	ltmp.value = *pos;
+	tmp = (struct list_head *)ltmp.parts[0];
+	tmp = tmp->next;
+	ltmp.parts[0] = (long)tmp;
+
+	*pos = ltmp.value;
+
+	if (tmp == &clk_list.k_list)
+		return NULL; /* No more to read */
+
+	return pos;
+}
+
+static void *clk_seq_start(struct seq_file *s, loff_t *pos)
+{
+	if (!*pos) { /* first call! */
+		union {
+			loff_t value;
+			long parts[2];
+		} ltmp;
+		ltmp.parts[0] = (long) clk_list.k_list.next;
+		*pos = ltmp. value;
+		return pos;
+	}
+	--(*pos); /* to realign *pos value! */
+
+	return clk_seq_next(s, NULL, pos);
+}
+
+static int clk_seq_show(struct seq_file *s, void *v)
+{
+	unsigned long *l = (unsigned long *)v;
+	struct list_head *node = (struct list_head *)(*l);
+	struct clk *clk = container_of(node, struct clk, node.n_node);
+	unsigned long rate = clk_get_rate(clk);
+
+	if (unlikely(!rate && !clk->parent))
+		return 0;
+
+	seq_printf(s, "%-12s\t: %ld.%02ldMHz - ", clk->name,
+	       rate / 1000000, (rate % 1000000) / 10000);
+	seq_printf(s, "[0x%p]", clk);
+	if (clk_is_enabled(clk))
+		seq_printf(s, " - enabled");
+
+	if (clk->parent)
+		seq_printf(s, " - [%s]", clk->parent->name);
+	seq_printf(s, "\n");
+
+	return 0;
+}
+
+static void clk_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static const struct seq_operations clk_seq_ops = {
+	.start = clk_seq_start,
+	.next = clk_seq_next,
+	.stop = clk_seq_stop,
+	.show = clk_seq_show,
+};
+
+static int clk_proc_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &clk_seq_ops);
+}
+
+static const struct file_operations clk_proc_ops = {
+	.owner = THIS_MODULE,
+	.open = clk_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static int __init clk_proc_init(void)
+{
+	struct proc_dir_entry *p;
+
+	p = create_proc_entry("clocks", S_IRUGO, NULL);
+
+	if (unlikely(!p))
+		return -EINVAL;
+
+	p->proc_fops = &clk_proc_ops;
+
+	return 0;
+}
+
+subsys_initcall(clk_proc_init);
+#endif
+
+#ifdef CONFIG_SYSFS
+static ssize_t clk_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+
+	return sprintf(buf, "%u\n", (unsigned int)clk_get_rate(clk));
+}
+
+static ssize_t clk_rate_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	unsigned long rate = simple_strtoul(buf, NULL, 10);
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+
+	if (rate) {
+		if (!clk_is_enabled(clk))
+			clk_enable(clk);
+		if (clk_set_rate(clk, rate) < 0)
+			return -EINVAL;
+	} else
+		clk_disable(clk);
+	return count;
+}
+
+static const char *clk_ctrl_token[] = {
+	"auto_switching",
+	"no_auto_switching",
+	"allow_propagation",
+	"no_allow_propagation",
+	"follow_parent",
+	"no_follow_parent",
+};
+static ssize_t clk_state_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+	ssize_t ret;
+
+
+	ret = sprintf(buf, "clock name: %s\n", clk->name);
+	if (clk_is_enabled(clk))
+		ret += sprintf(buf + ret, " + enabled\n");
+	else
+		ret += sprintf(buf + ret, " + disabled\n");
+	if (clk_is_readonly(clk))
+		ret += sprintf(buf + ret, " + rate read only\n");
+	else
+		ret += sprintf(buf + ret, " + rate writable\n");
+	ret +=
+	    sprintf(buf + ret, " + %s\n",
+		    clk_ctrl_token[(clk_allow_propagation(clk) ? 2 : 3)]);
+	ret +=
+	    sprintf(buf + ret, " + %s\n",
+		    clk_ctrl_token[(clk_is_auto_switching(clk) ? 0 : 1)]);
+	ret +=
+	    sprintf(buf + ret, " + %s\n",
+		    clk_ctrl_token[(clk_follow_parent(clk) ? 4 : 5)]);
+	ret +=
+	    sprintf(buf + ret, " + nr_clocks:  %u\n", clk->nr_clocks);
+	ret +=
+	    sprintf(buf + ret, " + nr_active_clocks:  %u\n",
+		clk->nr_active_clocks);
+	ret +=
+	    sprintf(buf + ret, " + nr_active_devices:  %u\n",
+		clk->nr_active_devices);
+	ret +=
+	    sprintf(buf + ret, " + rate: %u\n",
+		    (unsigned int)clk_get_rate(clk));
+	return ret;
+}
+
+static ssize_t clk_ctrl_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int idx, ret = 0;
+
+	ret += sprintf(buf + ret, "Allowed command:\n");
+
+	for (idx = 0; idx < ARRAY_SIZE(clk_ctrl_token); ++idx)
+		ret += sprintf(buf + ret, " + %s\n", clk_ctrl_token[idx]);
+
+	return ret;
+}
+static ssize_t clk_ctrl_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int i, idx_token, ret = -EINVAL;
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+
+	if (!count)
+		return ret;
+
+	for (i = 0, idx_token = -1; i < ARRAY_SIZE(clk_ctrl_token); ++i)
+		if (!strcmp(buf, clk_ctrl_token[i]))
+			idx_token = i;
+
+	if (idx_token == -EINVAL)
+		return ret;     /* token not valid... */
+
+	switch (idx_token) {
+	case 0:
+		clk->flags |= CLK_EVENT_PROPAGATES;
+		break;
+	case 1:
+		clk->flags &= ~CLK_EVENT_PROPAGATES;
+		break;
+	case 2:
+		clk->flags |= CLK_AUTO_SWITCHING;
+		if (!clk->nr_active_clocks && !clk->nr_active_devices)
+			clk_disable(clk);
+		else if (clk->nr_active_clocks || clk->nr_active_devices)
+			clk_enable(clk);
+		break;
+	case 3:
+		clk->flags &= ~CLK_AUTO_SWITCHING;
+		break;
+	case 4:
+		clk->flags |= CLK_FOLLOW_PARENT;
+		break;
+	case 5:
+		clk->flags &= ~CLK_FOLLOW_PARENT;
+		break;
+	}
+
+	return count;
+}
+
+static ssize_t clk_parent_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+	struct clk *parent = clk_get(NULL, buf);
+
+	if (!parent)
+		return -EINVAL;
+
+	clk_put(parent);
+	clk_set_parent(clk, parent);
+
+	return count;
+}
+
+static struct kobj_attribute attributes[] = {
+__ATTR(state, S_IRUSR, clk_state_show, NULL),
+__ATTR(rate, S_IRUSR | S_IWUSR, clk_rate_show, clk_rate_store),
+__ATTR(control, S_IRUSR | S_IWUSR, clk_ctrl_show, clk_ctrl_store),
+__ATTR(parent, S_IWUSR, NULL, clk_parent_store)
+};
+
+static struct attribute *clk_attrs[] = {
+	&attributes[0].attr,
+	&attributes[1].attr,
+	&attributes[2].attr,
+	&attributes[3].attr,
+	NULL
+};
+
+static struct attribute_group clk_attr_group = {
+	.attrs = clk_attrs,
+	.name = "attributes"
+};
+
+#if 0
+static inline char *_strsep(char **s, const char *d)
+{
+	int i, len = strlen(d);
+retry:
+	if (!(*s) || !(**s))
+		return NULL;
+	for (i = 0; i < len; ++i) {
+		if (**s != *(d+i))
+			continue;
+		++(*s);
+		goto retry;
+	}
+	return strsep(s, d);
+}
+
+/**
+ * clk_rates_store
+ *
+ * It parses the buf to create multi clocks transaction
+ * via user space
+ * The buffer has to be something like:
+ * clock_A @ rate_A; clock_B @ rate_b; clock_C @ rate_c
+ */
+static ssize_t clk_rates_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int i, ret;
+	int nclock = 0;
+	unsigned long *rates;
+	struct clk **clocks;
+
+	if (!buf)
+		return -1;
+
+	for (i = 0; i < count; ++i)
+		if (buf[i] == '@')
+			++nclock;
+
+	rates = kmalloc(sizeof(long) * nclock, GFP_KERNEL);
+	if (!rates)
+		return -ENOMEM;
+
+	clocks = kmalloc(sizeof(void *) * nclock, GFP_KERNEL);
+	if (!clocks) {
+		ret = -ENOMEM;
+		goto err_0;
+	}
+
+	/* Parse the buffer */
+	for (i = 0; i < nclock; ++i) {
+		char *name;
+		char *nrate;
+		name  = _strsep((char **)&buf, "@ "); ++buf;
+		nrate = _strsep((char **)&buf, " ;"); ++buf;
+		if (!name || !nrate) {
+			ret = -EINVAL;
+			goto err_1;
+			}
+		clocks[i] = clk_get(NULL, name);
+		rates[i]  = simple_strtoul(nrate, NULL, 10);
+		if (!clocks[i]) { /* the clock doesn't exist! */
+			ret = -EINVAL;
+			goto err_1;
+			}
+	}
+
+	ret = clk_set_rates(clocks, rates, nclock);
+	if (ret >= 0)
+		ret = count; /* to say OK */
+
+err_1:
+	kfree(clocks);
+err_0:
+	kfree(rates);
+	return ret;
+}
+
+static struct kobj_attribute clk_rates_attr =
+	__ATTR(rates, S_IWUSR, NULL, clk_rates_store);
+#endif
+
+static int __init clk_add_attributes(struct clk *clk, void *data)
+{
+	int ret;
+
+	ret = sysfs_update_group(&clk->kobj, &clk_attr_group);
+
+	return ret;
+}
+
+static int __init clk_late_init(void)
+{
+	int ret;
+
+	ret = clk_for_each(clk_add_attributes, NULL);
+
+	return ret;
+}
+
+late_initcall(clk_late_init);
+#endif
diff --git a/drivers/base/init.c b/drivers/base/init.c
index 7bd9b6a..2441b26 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -24,6 +24,7 @@ void __init driver_init(void)
 	buses_init();
 	classes_init();
 	firmware_init();
+	clock_init();
 	hypervisor_init();
 
 	/* These are also core pieces, but must come after the
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 8b4708e..550d993 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -17,6 +17,8 @@
 #include <linux/bootmem.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
+#include "clk.h"
 
 #include "base.h"
 
@@ -272,9 +274,20 @@ int platform_device_add(struct platform_device *pdev)
 	pr_debug("Registering platform device '%s'. Parent at %s\n",
 		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
 
+#ifdef CONFIG_GENERIC_CLK_FM
+	clk_add_device(pdev, PDEV_ADDING);
+
+	ret = device_add(&pdev->dev);
+
+	clk_add_device(pdev, (ret ? PDEV_ADD_FAILED : PDEV_ADDED));
+
+	if (ret == 0)
+		return ret;
+#else
 	ret = device_add(&pdev->dev);
 	if (ret == 0)
 		return ret;
+#endif
 
  failed:
 	while (--i >= 0) {
@@ -311,6 +324,9 @@ void platform_device_del(struct platform_device *pdev)
 			if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
 				release_resource(r);
 		}
+#ifdef CONFIG_GENERIC_CLK_FM
+	clk_del_device(pdev);
+#endif
 	}
 }
 EXPORT_SYMBOL_GPL(platform_device_del);
@@ -445,7 +461,18 @@ static int platform_drv_probe(struct device *_dev)
 	struct platform_driver *drv = to_platform_driver(_dev->driver);
 	struct platform_device *dev = to_platform_device(_dev);
 
+#ifdef CONFIG_GENERIC_CLK_FM
+	int ret;
+	ret = clk_probe_device(dev, PDEV_PROBEING);
+	if (ret)
+		return ret;
+	ret = drv->probe(dev);
+
+	clk_probe_device(dev, (ret ? PDEV_PROBE_FAILED : PDEV_PROBED));
+	return ret;
+#else
 	return drv->probe(dev);
+#endif
 }
 
 static int platform_drv_probe_fail(struct device *_dev)
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 1db9bbf..e537bcd 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -12,6 +12,7 @@
 #define __LINUX_CLK_H
 
 struct device;
+struct platform_device;
 
 /*
  * The base API.
@@ -142,4 +143,254 @@ struct clk *clk_get_parent(struct clk *clk);
  */
 struct clk *clk_get_sys(const char *dev_id, const char *con_id);
 
+/**
+ * clk_set_rates - set the clock rates
+ * @clk: clocks source
+ * @rate: desired clock rates in Hz
+ * @nclks: the number of clocks
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_rates(struct clk **clk, unsigned long *rates, unsigned long nclks);
+
+#ifndef CONFIG_GENERIC_CLK_FM
+
+#define bind_clock(_clk)
+#define pdevice_setclock(_dev, _clk)
+#define pdevice_setclock_byname(_dev, _clkname)
+#define pdevice_num_clocks(_dev)
+#define pdevice_clock(dev, idx)
+
+#else
+
+#include <linux/kobject.h>
+#include <linux/klist.h>
+#include <linux/notifier.h>
+#include <linux/pm.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+
+/**
+ * Clock operation -
+ *
+ * It's a set of function pointer to identify all the capability on a clock
+ */
+struct clk_ops {
+/** @init initializes the clock	*/
+	int (*init)(struct clk *);
+/** @enable enables the clock	*/
+	int (*enable)(struct clk *);
+/** @disable disables the clock	*/
+	int (*disable)(struct clk *);
+/** @set_rate sets the new frequency rate */
+	int (*set_rate)(struct clk *, unsigned long value);
+/** @set_parent sets the new parent clock */
+	int (*set_parent)(struct clk *clk, struct clk *parent);
+/** @recalc updates the clock rate when the parent clock is updated	 */
+	void (*recalc)(struct clk *);
+/** @round returns the allowed rate on the required value	*/
+	unsigned long (*round)(struct clk *, unsigned long value);
+/** @eval evaluates the clock rate based on a parent_rate but the
+ * real clock rate is __not__ changed
+ */
+	unsigned long (*eval)(struct clk *, unsigned long parent_rate);
+};
+
+/**
+ * struct clk - clock object
+ */
+struct clk {
+	spinlock_t		lock;
+
+	struct kobject		kobj;
+	struct kobject		*kdevices;
+
+	int			id;
+
+	const char		*name;
+	struct module		*owner;
+
+	struct clk		*parent;
+	struct clk_ops		*ops;
+
+	void			*private_data;
+
+	unsigned long		rate;
+	unsigned long		flags;
+
+	unsigned int		nr_active_clocks;
+	unsigned int		nr_active_devices;
+	unsigned int		nr_clocks;
+
+	void			*towner;/* the transaction owner of the clock */
+
+	struct klist		childs;
+	struct klist		devices;
+
+	struct klist_node	node;		/* for global link	*/
+	struct klist_node	child_node;	/* for child link	*/
+};
+
+#define CLK_ALWAYS_ENABLED		(0x1 << 0)
+#define CLK_EVENT_PROPAGATES		(0x1 << 1)
+#define CLK_RATE_PROPAGATES		CLK_EVENT_PROPAGATES
+/* CLK_AUTO_SWITCHING: enable/disable the clock based on the
+ * current active children
+ */
+#define CLK_AUTO_SWITCHING		(0x1 << 2)
+/* CLK_FOLLOW_PARENT: enable/disable the clock as the parent is
+ * enabled/disabled
+ */
+#define CLK_FOLLOW_PARENT		(0x1 << 3)
+
+/*
+ * Flags to support the system standby
+ */
+#define CLK_PM_EXP_SHIFT	(24)
+#define CLK_PM_EXP_NRBITS	(7)
+#define CLK_PM_RATIO_SHIFT	(16)
+#define CLK_PM_RATIO_NRBITS	(8)
+#define CLK_PM_EDIT_SHIFT	(31)
+#define CLK_PM_EDIT_NRBITS	(1)
+#define CLK_PM_TURNOFF		(((1<<CLK_PM_EXP_NRBITS)-1) << CLK_PM_EXP_SHIFT)
+
+int early_clk_register(struct clk *);
+/**
+ * Registers a new clock into the system
+ */
+int clk_register(struct clk *);
+/**
+ * Unregisters a clock into the system
+ */
+int clk_unregister(struct clk *);
+
+/**
+ * Returns the clock rate if the  parent clock is 'parent_rate'
+ */
+unsigned long clk_evaluate_rate(struct clk *, unsigned long parent_rate);
+
+#define CLK_UNDEFINED_RATE	(-1UL)
+/**
+ * Utility functions in the clock framework
+ */
+int clk_for_each(int (*fn)(struct clk *, void *), void *);
+
+int clk_for_each_child(struct clk *, int (*fn)(struct clk *, void *), void *);
+
+/** struct pdev_clk_info -
+ *
+ *  It's a meta data used to link the device of linux driver model
+ *  to the clock framework.
+ *  The device driver developers has to set only the clk field
+ *  all the other fileds are managed in the clk core code
+ */
+struct pdev_clk_info {
+	/** the device owner    */
+	struct platform_device  *pdev;
+	/** the clock address	*/
+	struct clk		*clk;
+	/** used by the clock core*/
+	struct klist_node	node;
+};
+
+/******************** clk transition notifiers *******************/
+#define	NOTIFY_CLK_ENTERCHANGE	0x1
+#define	NOTIFY_CLK_PRECHANGE	0x2
+#define	NOTIFY_CLK_POSTCHANGE	0x4
+#define NOTIFY_CLK_EXITCHANGE	0x8
+
+/** struct clk_event
+ *
+ * It's the object propagated during a clock transaction.
+ * During a transaction each device will receive an array of 'struct clk_event'
+ * based on the clocks it uses
+ */
+struct clk_event {
+	/** on which clock the event is		*/
+	struct clk *clk;
+	/** the clock rate before the event	*/
+	unsigned long old_rate;
+	/** the clock rate after the event	*/
+	unsigned long new_rate;
+};
+
+enum clk_event_e {
+	_CLK_NOCHANGE,
+	_CLK_ENABLE,
+	_CLK_DISABLE,
+	_CLK_CHANGE
+};
+
+/**
+ * clk_event_decode -
+ *
+ * @event: the events has to be decoded
+ * It's an utility function to identify what each clock
+ * is doing
+ */
+static inline enum clk_event_e clk_event_decode(struct clk_event const *event)
+{
+	if (event->old_rate == event->new_rate)
+		return _CLK_NOCHANGE;
+	if (!event->old_rate && event->new_rate)
+		return _CLK_ENABLE;
+	if (event->old_rate && !event->new_rate)
+		return _CLK_DISABLE;
+	return _CLK_CHANGE;
+}
+
+enum notify_ret_e {
+	NOTIFY_EVENT_HANDLED = 0,		/* event handled	*/
+	NOTIFY_EVENT_NOTHANDLED,		/* event not handled	*/
+};
+
+/* Some macro device oriented static initialization */
+#define bind_clock(_clk)					\
+	.nr_clks = 1,						\
+	.clks = (struct pdev_clk_info[]) { {			\
+		.clk = (_clk),					\
+		} },
+
+#define pdevice_setclock(_dev, _clk)				\
+	(_dev)->clks[0].clk = (_clk);				\
+	(_dev)->nr_clks = 1;
+
+#define pdevice_setclock_byname(_dev, _clkname)			\
+	(_dev)->clks[0].clk = clk_get(NULL, _clkname);		\
+	(_dev)->nr_clks = 1;
+
+#define pdevice_num_clocks(_dev)	((_dev)->nr_clks)
+
+#define pdevice_clock(dev, idx)		((dev)->clks[(idx)].clk)
+
+/**
+ * clk_generic_notify -
+ *
+ * @code: the code event
+ * @dev: the platform_device under transaction
+ * @data: the clock event descriptor
+ *
+ * it's a generic notify function for devie with _only_
+ * one clock. It will :
+ * - accept every 'ENTER' state
+ * - suspend on 'PRE' state
+ * - resume on 'POST' state
+ * - do nothing on 'EXIT' state
+ */
+int clk_generic_notify(unsigned long code, struct platform_device *dev,
+	void *data);
+
+/*
+ * clk_generic_evaluate_rate
+ *
+ * @clk: the analised clock
+ * @prate: the parent rate
+ *
+ * Evaluate the clock rate (without hardware modification) based on a 'prate'
+ * parent clock rate. It's based on 'divisor' relationship
+ * between parent and child
+ */
+unsigned long clk_generic_evaluate_rate(struct clk *clk, unsigned long prate);
+#endif
 #endif
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index b67bb5d..db1989d 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -12,6 +12,7 @@
 #define _PLATFORM_DEVICE_H_
 
 #include <linux/device.h>
+#include <linux/clk.h>
 #include <linux/mod_devicetable.h>
 
 struct platform_device {
@@ -22,6 +23,11 @@ struct platform_device {
 	struct resource	* resource;
 
 	struct platform_device_id	*id_entry;
+#ifdef CONFIG_GENERIC_CLK_FM
+	unsigned long	clk_state;      /* used by the core */
+	unsigned long	nr_clks;
+	struct pdev_clk_info    *clks;
+#endif
 };
 
 #define platform_get_device_id(pdev)	((pdev)->id_entry)
@@ -61,6 +67,9 @@ struct platform_driver {
 	int (*resume_early)(struct platform_device *);
 	int (*resume)(struct platform_device *);
 	struct device_driver driver;
+#ifdef CONFIG_GENERIC_CLK_FM
+	int (*notify)(unsigned long code, struct platform_device *, void *);
+#endif
 	struct platform_device_id *id_table;
 };
 
diff --git a/init/Kconfig b/init/Kconfig
index 0682ecc..4254c5f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1042,6 +1042,29 @@ config SLOW_WORK
 
 	  See Documentation/slow-work.txt.
 
+config GENERIC_CLK_FM
+        default n
+	depends on EXPERIMENTAL
+        bool "Generic Clock Framework"
+        help
+          Add the clock framework in the Linux driver model
+          to track the clocks used by each devices and drivers
+
+config CLK_FORCE_GENERIC_EVALUATE
+        depends on GENERIC_CLK_FM
+        default n
+        bool "Force the clk_generic_evaluate_rate"
+        help
+          Say the if you want use the clk_generic_evaluate_rate on every clock
+          without evaluate_rate
+
+config CLK_DEBUG
+        depends on GENERIC_CLK_FM
+        default n
+        bool "Debug the Generic Clk Framework"
+        help
+          Prints some message to debug the clock framework
+
 endmenu		# General setup
 
 config HAVE_GENERIC_DMA_COHERENT
-- 
1.6.2.5




^ permalink raw reply related

* [Proposal] [PATCH] generic clock framework
From: Francesco VIRLINZI @ 2009-11-10 14:00 UTC (permalink / raw)
  To: linux-arm-kernel, Linux-sh, linux-embedded


[-- Attachment #1.1: Type: text/plain, Size: 1841 bytes --]

Hi all

I'm Francesco and I work in STMicroelectronics

In the last ELC-E_2009 I spoke on a generic clock framework I'm working on
  (see 
http://tree.celinuxforum.org/CelfPubWiki/ELCEurope2009Presentations?action=AttachFile&do=view&target=ELC_E_2009_Generic_Clock_Framework.pdf).

I wrote the gcf to manage both clocks the platform_devices during a 
clock operation.

The main features are:
  - it's integrated in the LDM
  - it tracks the clock-to-clock relationship
  - it tracks the clock-to-device relationship

  - it has sysfs interface
  - - the user can navigate the clock tree under /sys/clocks/...

  - it uses the linux API (<linux/clk.h>) with some extra functions (to 
register/unregister a clock
    and other utility functions as clk_for_each())

  - it involves the platform_device and the platform_driver in the clock 
propagation.
  - - basically each clock operation is managed as a transaction which 
evolves step by step.
  - - all the clock rates are evaluated (before the clk operation is 
actually done)
  - - each platform_device can check (before the clk operation is 
actually done) the clk environment
      it will have at the end of clock operation and if required it can 
reject the operation.
  - - each clock operation is actually executed only if all the 
platform_devices accept the operation it-self


Moreover a common clock framework could be used to avoid a lot of 
duplicated and/or similar code
  just a grep of 'EXPORT_SYMBOL\(clk_enable' under arch/arm finds 22 
entries.

The patch is based on a 2.6.30 kernel also if it has a preliminary 
integration with the PM_RUNTIME
  support.

It works on our st40 (an sh4 cpu based system) no test/porting was done 
on any ARM platform.

It would be mainly a starting point for a discussion and I'm available 
to extend/fix/share it.

Regards
  Francesco

[-- Attachment #1.2: Type: text/html, Size: 2786 bytes --]

[-- Attachment #2: 0001-generic-clock-framework.patch --]
[-- Type: text/plain, Size: 77604 bytes --]

From 4e065fb9247ec511bfdc88001f0713977d3f4e89 Mon Sep 17 00:00:00 2001
From: Francesco Virlinzi <francesco.virlinzi@st.com>
Date: Fri, 23 Oct 2009 15:26:42 +0200
Subject: [PATCH] generic clock framework

version: 0.6.2

Signed-off-by: Francesco Virlinzi <francesco.virlinzi@st.com>
---
 drivers/base/Makefile           |    4 +
 drivers/base/base.h             |    5 +
 drivers/base/clk.c              | 1606 +++++++++++++++++++++++++++++++++++++++
 drivers/base/clk.h              |  319 ++++++++
 drivers/base/clk_pm.c           |  197 +++++
 drivers/base/clk_utils.c        |  456 +++++++++++
 drivers/base/init.c             |    1 +
 drivers/base/platform.c         |   27 +
 include/linux/clk.h             |  251 ++++++
 include/linux/platform_device.h |    9 +
 init/Kconfig                    |   23 +
 11 files changed, 2898 insertions(+), 0 deletions(-)
 create mode 100644 drivers/base/clk.c
 create mode 100644 drivers/base/clk.h
 create mode 100644 drivers/base/clk_pm.c
 create mode 100644 drivers/base/clk_utils.c

diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index b5b8ba5..b78a2bf 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -16,6 +16,10 @@ ifeq ($(CONFIG_SYSFS),y)
 obj-$(CONFIG_MODULES)	+= module.o
 endif
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+ifdef CONFIG_GENERIC_CLK_FM
+obj-y			+= clk.o clk_utils.o
+obj-$(CONFIG_PM)	+= clk_pm.o
+endif
 
 ifeq ($(CONFIG_DEBUG_DRIVER),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/base/base.h b/drivers/base/base.h
index b528145..bc5b9e8 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -94,6 +94,11 @@ extern int devices_init(void);
 extern int buses_init(void);
 extern int classes_init(void);
 extern int firmware_init(void);
+#ifdef CONFIG_GENERIC_CLK_FM
+extern int clock_init(void);
+#else
+static inline int clock_init(void){ return 0; }
+#endif
 #ifdef CONFIG_SYS_HYPERVISOR
 extern int hypervisor_init(void);
 #else
diff --git a/drivers/base/clk.c b/drivers/base/clk.c
new file mode 100644
index 0000000..7feae61
--- /dev/null
+++ b/drivers/base/clk.c
@@ -0,0 +1,1606 @@
+/*
+ * -------------------------------------------------------------------------
+ * clk.c
+ * -------------------------------------------------------------------------
+ * (C) STMicroelectronics 2008
+ * (C) STMicroelectronics 2009
+ * Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+ * -------------------------------------------------------------------------
+ * May be copied or modified under the terms of the GNU General Public
+ * License v.2 ONLY.  See linux/COPYING for more information.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/klist.h>
+#include <linux/sysdev.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include "clk.h"
+#include "base.h"
+
+#define CLK_NAME		"Generic Clk Framework"
+#define CLK_VERSION		"0.6.2"
+
+/* #define CLK_SAFE_CODE */
+
+klist_entry_support(clock, clk, node)
+klist_entry_support(child_clock, clk, child_node)
+klist_entry_support(dev_info, pdev_clk_info, node)
+
+#define to_clk(ptr)	container_of(ptr, struct clk, kobj)
+#define to_tnode(ptr)	container_of(ptr, struct clk_tnode, pnode)
+
+static int sysfs_clk_attr_show(struct kobject *kobj,
+				struct attribute *attr, char *buf)
+{
+	ssize_t ret = -EIO;
+	struct kobj_attribute *kattr
+	    = container_of(attr, struct kobj_attribute, attr);
+	if (kattr->show)
+		ret = kattr->show(kobj, kattr, buf);
+	return ret;
+}
+
+static ssize_t
+sysfs_clk_attr_store(struct kobject *kobj, struct attribute *attr,
+			const char *buf, size_t count)
+{
+	ssize_t ret = -EIO;
+	struct kobj_attribute *kattr
+	    = container_of(attr, struct kobj_attribute, attr);
+	if (kattr->store)
+		ret = kattr->store(kobj, kattr, buf, count);
+	return ret;
+}
+
+static struct sysfs_ops clk_sysfs_ops = {
+	.show = sysfs_clk_attr_show,
+	.store = sysfs_clk_attr_store,
+};
+
+static struct kobj_type ktype_clk = {
+	.sysfs_ops = &clk_sysfs_ops,
+};
+
+static struct clk *check_clk(struct clk *);
+
+static struct kobject *clk_kobj;
+static DEFINE_MUTEX(clk_list_sem);
+static atomic_t transaction_counter = ATOMIC_INIT(0);
+struct klist clk_list = KLIST_INIT(clk_list, NULL, NULL);
+
+klist_function_support(child, clk, child_node, kobj)
+klist_function_support(device, pdev_clk_info, node, pdev->dev.kobj)
+
+/*
+ * The ___clk_xxx operations doesn't raise propagation
+ * they are used to operate on the real clock
+ */
+static int
+__clk_operations(struct clk *clk, unsigned long rate,
+	enum clk_ops_id const id_ops)
+{
+	int ret = 0;
+	unsigned long *ops_fns = (unsigned long *)clk->ops;
+	if (likely(ops_fns && ops_fns[id_ops])) {
+		int (*fns)(struct clk *clk, unsigned long rate)
+			= (void *)ops_fns[id_ops];
+		unsigned long flags;
+		spin_lock_irqsave(&clk->lock, flags);
+		ret = fns(clk, rate);
+		spin_unlock_irqrestore(&clk->lock, flags);
+	}
+	return ret;
+}
+
+static inline int __clk_init(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_INIT);
+}
+static inline int __clk_enable(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_ENABLE);
+}
+static inline int __clk_disable(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_DISABLE);
+}
+static inline int __clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, rate, __CLK_SET_RATE);
+}
+static inline int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, (unsigned long)parent, __CLK_SET_PARENT);
+}
+static inline int __clk_recalc_rate(struct clk *clk)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, 0, __CLK_RECALC);
+}
+static inline int __clk_round(struct clk *clk, unsigned long value)
+{
+	pr_debug(": %s\n", clk->name);
+	return __clk_operations(clk, value, __CLK_ROUND);
+}
+
+static inline int __clk_eval(struct clk *clk, unsigned long prate)
+{
+#ifndef CONFIG_CLK_FORCE_GENERIC_EVALUATE
+	pr_debug(": %s\n", clk->name);
+	return	__clk_operations(clk, prate, __CLK_EVAL);
+#else
+	unsigned long rate, flags;
+	pr_debug(": %s\n", clk->name);
+	if (likely(clk->ops && clk->ops->eval)) {
+		spin_lock_irqsave(&clk->lock, flags);
+		rate = clk->ops->eval(clk, prate);
+		spin_unlock_irqrestore(&clk->lock, flags);
+	} else
+		rate = clk_generic_evaluate_rate(clk, prate);
+	return rate;
+#endif
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int
+clk_pm_runtime_devinfo(enum rpm_status code, struct pdev_clk_info *info)
+{
+	struct platform_device *pdev = info->pdev;
+
+	pr_debug("\n");
+
+	switch (code) {
+	case RPM_ACTIVE:
+		return clk_notify_child_event(CHILD_DEVICE_ENABLED, info->clk);
+	case RPM_SUSPENDED:
+		return clk_notify_child_event(CHILD_DEVICE_DISABLED, info->clk);
+	}
+	return -EINVAL;
+}
+
+int clk_pm_runtime_device(enum rpm_status code, struct platform_device *dev)
+{
+	int idx;
+	int ret = 0;
+	struct pdev_clk_info *info;
+
+	if (!dev)
+		return -EFAULT;
+
+	if (!dev->clks || !pdevice_num_clocks(dev))
+		return 0;
+
+	pr_debug("\n");
+/*
+ *	Check if the device is under a transaction.
+ * 	If so the GCFdoesn't raise a 'clk_pm_runtime_devinfo'
+ *	all the device change will be notified on 'tnode_transaction_complete'
+ *	if required....
+ */
+	if (atomic_read((atomic_t *)&dev->clk_flags)) {
+		pr_debug("%s.%d under transaction\n", dev->name, dev->id);
+		return ret;
+	}
+	for (idx = 0, info = dev->clks; idx < pdevice_num_clocks(dev); ++idx)
+		ret |= clk_pm_runtime_devinfo(&info[idx], state, 0);
+
+	return ret;
+}
+#else
+#define clk_pm_runtime_devinfo(x, y)
+#define clk_pm_runtime_device(x, y)
+#endif
+
+/**
+ * tnode_malloc
+ *
+ * Allocs the memory for both the transaction and the
+ * clk_event objects
+ */
+static struct clk_tnode *tnode_malloc(struct clk_tnode *parent,
+	unsigned long nevent)
+{
+	struct clk_event *evt;
+	struct clk_tnode *node;
+
+	if (nevent > 32)
+		return NULL;
+
+	node = kmalloc(sizeof(*node) + nevent *	sizeof(*evt), GFP_KERNEL);
+
+	if (!node)
+		return NULL;
+
+	evt = (struct clk_event *)(sizeof(struct clk_tnode) + (long)node);
+
+	node->tid    = atomic_inc_return(&transaction_counter);
+	node->parent = parent;
+	node->size   = nevent;
+	node->events = evt;
+	node->events_map = 0;
+	INIT_LIST_HEAD(&node->childs);
+
+	return node;
+}
+
+/**
+ * tnode_free
+ *
+ * Free the tnode memory
+ */
+static void tnode_free(struct clk_tnode *node)
+{
+	if (tnode_get_parent(node)) {
+		list_del(&node->pnode);
+		kfree(node);
+	}
+}
+
+/**
+ *  tnode_check_clock -
+ *
+ *  @node: the tnode object
+ *  @clk:  the clock object
+ *
+ *  returns a boolean value
+ *  it checks if the clock (clk) is managed by the
+ *  tnode (node) or any parent node
+ */
+static int __must_check
+tnode_check_clock(struct clk_tnode *node, struct clk *clk)
+{
+	int j;
+	for (; node; node = tnode_get_parent(node))
+		/* scans all the event */
+		tnode_for_each_valid_events(node, j)
+			if (tnode_get_clock(node, j) == clk)
+					return 1; /* FOUND!!! */
+	return 0;
+}
+
+/**
+  * tnode_lock_clocks -
+  *
+  * @node: the tnode object
+  *
+  * marks all the clocks under transaction to be sure there is no more
+  * than one transaction for each clock
+  */
+static int __must_check
+tnode_lock_clocks(struct clk_tnode *node)
+{
+	int i;
+	pr_debug("\n");
+
+	/* 1. try to mark all the clocks in transaction */
+	for (i = 0; i < tnode_get_size(node); ++i)
+		if (clk_set_towner(tnode_get_clock(node, i), node)) {
+			struct clk *clkp = tnode_get_clock(node, i);
+			/* this clock is already locked */
+			/* we accept that __only__ if it is locked by a
+			 * parent tnode!!!
+			 */
+			if (!tnode_get_parent(node)) {
+				pr_debug("Error clk %s locked but "
+					  "there is no parent!\n", clkp->name);
+				goto err_0;
+			}
+			pr_debug("clk %s already locked\n", clkp->name);
+			if (tnode_check_clock(tnode_get_parent(node), clkp)) {
+				pr_debug("ok clk %s locked "
+					  "by a parent\n", clkp->name);
+				continue;
+			} else
+				goto err_0;
+		} else
+			/* set the event as valid in the bitmap*/
+			tnode_set_map_id(node, i);
+
+/*
+ * all the clocks are marked succesfully or all the clock on
+ * this tnode are already managed by parent
+ */
+	if (!tnode_get_map(node)) { /* check if the bitamp is not zero */
+		if (tnode_get_parent(node))
+			kfree(node);
+		return 1;
+	}
+
+ /*
+ * all the clocks are marked succesfully _and_ there is at least
+ * one clock marked.
+ * Add the tnode to its parent! and return
+ */
+	if (tnode_get_parent(node))
+		list_add_tail(&node->pnode, &tnode_get_parent(node)->childs);
+
+	return 0;
+
+err_0:
+	pr_debug("Error on clock locking...\n");
+	for (--i; i >= 0; --i)
+		if (tnode_check_map_id(node, i))
+			clk_clean_towner(tnode_get_clock(node, i));
+
+	if (tnode_get_parent(node))
+		kfree(node);
+
+	return -EINVAL;
+}
+
+/**
+ * tnode_transaction_complete -
+ *
+ * checks the devices status when the transaction is complete.
+ */
+static void tnode_transaction_complete(struct clk_tnode *node)
+{
+	struct klist_iter i;
+	struct pdev_clk_info *dev_info;
+	int j;
+
+	pr_debug("tid: %d\n", (int)tnode_get_id(node));
+	tnode_for_each_valid_events(node, j) {
+	klist_iter_init(&tnode_get_clock(node, j)->devices, &i);
+	while ((dev_info = next_dev_info(&i))) {
+		/* update the device state */
+		struct platform_device *dev = dev_info->pdev;
+		switch (dev->clk_state & (DEV_SUSPENDED_ON_TRANSACTION |
+					  DEV_RESUMED_ON_TRANSACTION)) {
+		case 0: /* this device doesn't care on the clock transaction */
+			atomic_clear_mask(DEV_ON_TRANSACTION,
+				(atomic_t *)&dev->clk_state);
+			break;
+
+		case (DEV_SUSPENDED_ON_TRANSACTION |
+			DEV_RESUMED_ON_TRANSACTION):
+			/* this device was suspended and
+			 * resumed therefore no real change
+			 */
+			pr_debug("dev: %s.%d "
+				"Suspended&Resumed (no child event)\n",
+				dev->name, dev->id);
+			atomic_clear_mask(DEV_ON_TRANSACTION |
+					  DEV_SUSPENDED_ON_TRANSACTION |
+					  DEV_RESUMED_ON_TRANSACTION,
+					  (atomic_t *)&dev->clk_state);
+			break;
+		case DEV_SUSPENDED_ON_TRANSACTION:
+			atomic_clear_mask(DEV_ON_TRANSACTION |
+				DEV_SUSPENDED_ON_TRANSACTION,
+				(atomic_t *)&dev->clk_state);
+			pr_debug("dev: %s.%d Suspended\n",
+				dev->name, dev->id);
+			clk_pm_runtime_device(RPM_SUSPENDED, dev);
+			break;
+		case DEV_RESUMED_ON_TRANSACTION:
+			atomic_clear_mask(DEV_ON_TRANSACTION |
+				DEV_RESUMED_ON_TRANSACTION,
+				(atomic_t *)&dev->clk_state);
+			pr_debug("dev: %s.%d Resumed\n",
+				dev->name, dev->id);
+			clk_pm_runtime_device(RPM_ACTIVE, dev);
+			break;
+
+		default:
+			printk(KERN_ERR "%s: device %s,%d clk_flags _not_ valid %u\n",
+				__func__, dev->name, dev->id,
+				(unsigned int)dev->clk_state);
+		}
+	}
+	klist_iter_exit(&i);
+	clk_clean_towner(tnode_get_clock(node, j));
+	}
+	pr_debug("tid: %d exit\n", (int)tnode_get_id(node));
+	return;
+}
+
+/*
+ * Check if the clk is registered
+ */
+#ifdef CLK_SAFE_CODE
+static struct clk *check_clk(struct clk *clk)
+{
+	struct clk *clkp;
+	struct clk *result = NULL;
+	struct klist_iter i;
+
+	pr_debug("\n");
+
+	klist_iter_init(&clk_list, &i);
+	while ((clkp = next_clock(&i)))
+		if (clk == clkp) {
+			result = clk;
+			break;
+		}
+	klist_iter_exit(&i);
+	return result;
+}
+#else
+static inline struct clk *check_clk(struct clk *clk)
+{
+	return clk;
+}
+#endif
+
+enum child_event_e {
+	CHILD_CLOCK_ENABLED = 1,
+	CHILD_CLOCK_DISABLED,
+	CHILD_DEVICE_ENABLED,
+	CHILD_DEVICE_DISABLED,
+};
+
+static int
+clk_notify_child_event(enum child_event_e const code, struct clk *clk)
+{
+	if (!clk)
+		return 0;
+
+	switch (code) {
+	case CHILD_CLOCK_ENABLED:
+		++clk->nr_active_clocks;
+		break;
+	case CHILD_CLOCK_DISABLED:
+		--clk->nr_active_clocks;
+		break;
+	case CHILD_DEVICE_ENABLED:
+		++clk->nr_active_devices;
+		break;
+	case CHILD_DEVICE_DISABLED:
+		--clk->nr_active_devices;
+		break;
+	}
+
+	if (clk_is_auto_switching(clk)) {
+		/*
+		 * Check if there are still users
+		 */
+		if (!clk->nr_active_devices && !clk->nr_active_clocks)
+			clk_disable(clk);
+		else if (!clk_get_rate(clk)) /* if off.. turn-on */
+			clk_enable(clk);
+	}
+
+	return 0;
+}
+
+/**
+ * clk_dev_events_malloc -
+ *
+ * builds a struct clk_event array (dev_event).
+ * the array size (how many elements) is based on device_num_clocks(dev)
+ * the contenets of each element is equal to:
+ * - the events array (if the idx-clock is under transaction)
+ * - the current clock setting if the idx-clock isn't under transaction
+ */
+static struct clk_event * __must_check
+clk_dev_events_malloc(struct platform_device const *dev)
+{
+	struct clk_event *dev_events;
+	struct clk_tnode *node;
+	int i, j;
+	pr_debug("\n");
+/*
+ * 1.  simple case:
+ *	- device_num_clocks(dev) = 1
+ */
+	if (pdevice_num_clocks(dev) == 1) {
+		node = (struct clk_tnode *)pdevice_clock(dev, 0)->towner;
+		for (i = 0; i < tnode_get_size(node); ++i)
+			if (tnode_get_clock(node, i) == pdevice_clock(dev, 0))
+				return tnode_get_event(node, i);
+	}
+/*
+ * 2. - device_num_clocks(dev) > 1
+ *	GCF has to build a dedicated device events (devents) array
+ *	for this device! sorted as the device registered it-self!
+ */
+	dev_events = kmalloc(sizeof(*dev_events) * pdevice_num_clocks(dev),
+			GFP_KERNEL);
+	if (!dev_events)
+		return NULL;
+
+	for (i = 0; i < pdevice_num_clocks(dev); ++i) {
+		node = (struct clk_tnode *)pdevice_clock(dev, i)->towner;
+		dev_events[i].clk = pdevice_clock(dev, i);
+		if (!node) {/* this means this clocs isn't under transaction */
+		     dev_events[i].old_rate =
+				clk_get_rate(pdevice_clock(dev, i));
+		     dev_events[i].new_rate =
+				clk_get_rate(pdevice_clock(dev, i));
+		     continue;
+		}
+		/* search the right clk_event */
+		for (j = 0; tnode_get_clock(node, j) != pdevice_clock(dev, i);
+		     ++j);
+
+		dev_events[i].old_rate = tnode_get_event(node, j)->old_rate;
+		dev_events[i].new_rate = tnode_get_event(node, j)->new_rate;
+	}
+	return dev_events;
+}
+
+/**
+ * clk_devents_free -
+ * free the devent allocated on the device dev.
+ */
+static inline void
+clk_dev_events_free(struct clk_event *dev_events, struct platform_device *dev)
+{
+	if (pdevice_num_clocks(dev) == 1)
+		return ;
+	kfree(dev_events);
+}
+
+/**
+ * clk_trnsc_fsm -
+ *
+ * propagate the transaction to all the childs
+ * each transaction has the following life-time:
+ *
+ *	+---------------+
+ *	|    ENTER_CLK	|   The ENTER state only for clocks
+ *	+---------------+     - acquires all the clock of the transaction
+ *		|	       - builds the transaction graph
+ *		|	      - for each clock generates a child transaction
+ *		|
+ *   +---------------------+
+ *   |	+---------------+  |
+ *   |	|    ENTER_DEV 	|  |  The ENTER state only for devices
+ *   |  +---------------+  |  - >> NOTIFY_CLK_ENTERCHANGE << notified
+ *   |		|	   |  - - the device could refuse the operation
+ *   |		|	   |
+ *   |	+---------------+  |
+ *   |	|    PRE_DEV	|  |  The PRE state only devices
+ *   |	+---------------+  |  - >> NOTIFY_CLK_PRECHANGE << notified
+ *   |		|	   |  - - the device could be suspended
+ *   +---------------------+
+ *		|
+ *	+---------------+
+ * 	|   CHANGE_CLK	|    The CHANGE state only for clocks
+ *	+---------------+     - updates all the physical clocks
+ *		|	        and relative clk_event_s according to
+ *		|	        the hw value.
+ *   +---------------------+
+ *   |		|	   |
+ *   |	+---------------+  |
+ *   |	|   POST_DEV	|  |  The POST state only for devices
+ *   |  +---------------+  |  - >> NOTIFY_CLK_POSTCHANGE << notified
+ *   |		|	   |  - - the devices could be resumed
+ *   |		|	   |
+ *   |	+---------------+  |
+ *   |	|  EXIT_DEV	|  |   The EXIT state only for devices
+ *   |  +---------------+  |   - >> NOTIFY_CLK_EXITCHANGE << notified
+ *   |		|	   |   - - the devices is aware all the other
+ *   +---------------------+	   devices are resumed.
+ *		|
+ *	+---------------+
+ *	|  EXIT_CLK	|      The EXIT state only for clocks
+ *	+---------------+      (to free all the memory)
+ *				- Free all the allocated memory
+ *
+ */
+
+static enum notify_ret_e
+clk_trnsc_fsm(enum clk_fsm_e const code, struct clk_tnode *node)
+{
+	struct pdev_clk_info *dev_info;
+	struct clk_tnode *tchild;
+	struct klist_iter i;
+	int j;
+	enum notify_ret_e tmp, ret_notifier = NOTIFY_EVENT_HANDLED;
+
+#ifdef CONFIG_CLK_DEBUG
+	switch (code) {
+	case TRNSC_ENTER_CLOCK:
+	case TRNSC_ENTER_DEVICE:
+		printk(KERN_INFO "ENTER_%s ",
+			(code == TRNSC_ENTER_CLOCK ? "CLK" : "DEV"));
+		break;
+	case TRNSC_PRE_DEVICE:
+		printk(KERN_INFO "PRE_DEV ");
+		break;
+	case TRNSC_CHANGE_CLOCK:
+		printk(KERN_INFO "CHANGE_CLK ");
+		break;
+	case TRNSC_POST_DEVICE:
+		printk(KERN_INFO "POST_DEV ");
+		break;
+	case TRNSC_EXIT_DEVICE:
+	case TRNSC_EXIT_CLOCK:
+		printk(KERN_INFO "EXIT_%s ",
+			(code == TRNSC_EXIT_DEVICE ? "DEV" : "CLK"));
+			break;
+	}
+	printk(KERN_INFO"tid:%u ", (unsigned int)tnode_get_id(node));
+	if (tnode_get_parent(node))
+		printk(KERN_INFO " (tpid: %d)",
+			(int)tnode_get_id(tnode_get_parent(node)));
+	printk(KERN_INFO " (0x%x/0x%x) ", (unsigned int)tnode_get_size(node),
+			(unsigned int)tnode_get_map(node));
+	for (j = 0; j < tnode_get_size(node); ++j) {
+		if (tnode_check_map_id(node, j))
+			/* print only the valid event... */
+			printk(KERN_INFO"- %s ",
+				tnode_get_clock(node, j)->name);
+		else if (code == TRNSC_ENTER_CLOCK)
+			printk(KERN_INFO"- %s ",
+				tnode_get_clock(node, j)->name);
+	}
+	printk(KERN_INFO"\n");
+#endif
+
+	/* 
+	 * Clk ENTER state
+	 */
+	if (code == TRNSC_ENTER_CLOCK) {
+		unsigned long idx;
+		enum clk_event_e sub_code;
+		struct clk *clkp;
+		struct clk_event *sub_event = NULL;
+
+		/* first of all the GCF tries to lock the clock of this tnode
+		 * and links the tnode to its parent (if any)
+		 */
+		switch (tnode_lock_clocks(node)) {
+		case 0:
+			break;
+		case -EINVAL:
+			return NOTIFY_EVENT_NOTHANDLED;
+		case 1:
+			return NOTIFY_EVENT_HANDLED;
+		}
+
+		pr_debug("clocks acquired\n");
+		/* Propagates the events to the sub clks */
+		tnode_for_each_valid_events(node, j) {
+
+		if (!clk_allow_propagation(tnode_get_clock(node, j))) {
+			pr_debug("clk: %s doesn't want propagation\n",
+				tnode_get_clock(node, j)->name);
+			continue;
+		}
+		if (!(tnode_get_clock(node, j)->nr_clocks))
+			continue;
+
+		tchild = tnode_malloc(node,
+			tnode_get_clock(node, j)->nr_clocks);
+		if (!tchild) {
+			printk(KERN_ERR "No enough memory during a clk "
+					"transaction\n");
+			ret_notifier |= NOTIFY_EVENT_NOTHANDLED;;
+			return ret_notifier;
+		}
+
+		pr_debug("memory for child transaction acquired\n");
+		idx = 0;
+		sub_code = clk_event_decode(tnode_get_event(node, j));
+		klist_iter_init(&tnode_get_clock(node, j)->childs, &i);
+		while ((clkp = next_child_clock(&i))) {
+			sub_event = tnode_get_event(tchild, idx);
+			clk_event_init(sub_event, clkp, clk_get_rate(clkp),
+				clk_get_rate(clkp));
+			switch (sub_code) {/* prepare the sub event fields */
+			case _CLK_CHANGE:
+			case _CLK_ENABLE:
+				sub_event->new_rate = clk_evaluate_rate(clkp,
+					tnode_get_event(node, j)->new_rate);
+				break;
+			case _CLK_DISABLE:
+				sub_event->new_rate = 0;
+				break;
+			case _CLK_NOCHANGE:
+				break;
+			}
+			++idx;
+			}
+		klist_iter_exit(&i);
+		/* now GCF can araiese the sub transaction */
+		ret_notifier |=
+			clk_trnsc_fsm(code, tchild);
+		}
+		return ret_notifier;
+	}
+
+	/*
+	 * Clk CHANGE state
+	 */
+	if (code == TRNSC_CHANGE_CLOCK) {
+		/* the clocks on the root node are managed directly in the
+		 * clk_set_rate/clk_enable/... functions ...
+		 * while all the other clocks have to managed here!
+		 */
+		if (node->parent)
+			tnode_for_each_valid_events(node, j) {
+				struct clk_event *event;
+				long code;
+				event = tnode_get_event(node, j);
+				code = clk_event_decode(event);
+				switch (code) {
+				case _CLK_CHANGE:
+					__clk_recalc_rate(event->clk);
+					event->new_rate =
+						clk_get_rate(event->clk);
+					break;
+				case _CLK_ENABLE:
+					if (clk_follow_parent(event->clk)) {
+						__clk_enable(event->clk);
+						event->new_rate =
+						clk_get_rate(event->clk);
+					}
+					break;
+				case _CLK_DISABLE:
+					if (clk_is_enabled(event->clk))
+						__clk_disable(event->clk);
+					break;
+				}
+			}
+
+		list_for_each_entry(tchild, &node->childs, pnode)
+			ret_notifier |= clk_trnsc_fsm(code, tchild);
+
+		return ret_notifier;
+	}
+
+	/*
+	 * Clk EXIT state
+	 */
+	if (code == TRNSC_EXIT_CLOCK) {
+		struct list_head *ptr, *next;
+		/* scans all the transaction childs */
+		list_for_each_safe(ptr, next, &node->childs)
+			clk_trnsc_fsm(code, to_tnode(ptr));
+
+		/* update the devices/clocks state */
+		tnode_transaction_complete(node);
+
+		tnode_free(node);
+		pr_debug("EXIT_CLK complete\n");
+
+		return ret_notifier;
+	}
+
+	/*
+	 * Here the devices management
+	 */
+	tnode_for_each_valid_events(node, j) {
+		if (!clk_allow_propagation(tnode_get_clock(node, j)))
+			continue;
+	klist_iter_init(&tnode_get_clock(node, j)->devices, &i);
+	while ((dev_info = next_dev_info(&i))) {
+		struct platform_device *pdev = dev_info->pdev;
+		struct platform_driver *pdrv = 	container_of(
+			pdev->dev.driver, struct platform_driver, driver);
+
+		struct clk_event *dev_events;
+
+		if (!pdrv || !pdrv->notify) {
+			pr_debug(
+			"device %s.%d registered with no notify function\n",
+				pdev->name, pdev->id);
+			continue;
+		}
+		/* check if it already had a 'code' event */
+		if (pdev_transaction_move_on(pdev, code))
+			continue;
+
+		dev_events = clk_dev_events_malloc(pdev);
+		if (!dev_events) {
+			printk(KERN_ERR"%s: No Memory during a clk "
+				"transaction\n", __func__);
+			continue;
+		}
+
+		/* GCF can use 'code' directly in the .notify function
+		 * just because external 'NOTIFY_CLK_xxxCHANGE' code
+		 * matchs with the internal 'device' code
+		 */
+		tmp = pdrv->notify(code, pdev, dev_events);
+		clk_dev_events_free(dev_events, pdev);
+		ret_notifier |= tmp;
+#ifdef CONFIG_PM_RUNTIME
+		if (code == TRNSC_PRE_DEVICE && tmp == NOTIFY_EVENT_HANDLED) {
+			printk(KERN_INFO "clk %s on code %u suspends "
+				"device %s.%d\n",
+				transaction_get_clock(node, j)->name,
+				(unsigned int)code, pdev->name, pdev->id);
+			pm_runtime_suspend(&pdev->dev);
+		} else
+		if (code == TRNSC_POST_DEVICE && tmp == NOTIFY_EVENT_HANDLED) {
+			printk(KERN_INFO "clk %s on code %u resumes "
+				"device %s.%d\n",
+				transaction_get_clock(node, j)->name,
+				(unsigned int)code, pdev->name, pdev->id);
+			pm_runtime_resume(&pdev->dev);
+		};
+#endif
+	} /* while closed */
+	klist_iter_exit(&i);
+	} /* for closed */
+
+	/*
+	 *and propagate down...
+	 */
+	list_for_each_entry(tchild, &node->childs, pnode)
+			ret_notifier |= clk_trnsc_fsm(code, tchild);
+
+	return ret_notifier;
+}
+
+static void clk_initialize(struct clk *clk)
+{
+	kobject_init(&clk->kobj, &ktype_clk);
+	kobject_set_name(&clk->kobj, "%s", clk->name);
+	kobject_get(&clk->kobj);
+
+	clk->nr_clocks = 0;
+	clk->nr_active_clocks = 0;
+	clk->nr_active_devices = 0;
+	clk->towner = NULL;
+
+	klist_init(&clk->childs, klist_get_child, klist_put_child);
+	klist_init(&clk->devices, klist_get_device, klist_put_device);
+
+}
+
+/**
+  * clk_register -
+  *
+  * registers a new clk in the system.
+  * returns zero if success
+  */
+int clk_register(struct clk *clk)
+{
+	int ret = 0;
+	if (!clk)
+		return -EFAULT;
+	pr_debug("%s\n", clk->name);
+
+	clk_initialize(clk);
+
+	/* Initialize ... */
+	__clk_init(clk);
+
+	if (clk->parent) {
+#ifdef CLK_SAFE_CODE
+		/* 1. the parent has to be registered */
+		if (!check_clk(clk->parent))
+			return -ENODEV;
+		/* 2. an always enabled child has to sit on a always
+		 *    enabled parent!
+		 */
+		if (clk->flags & CLK_ALWAYS_ENABLED &&
+			!(clk->parent->flags & CLK_ALWAYS_ENABLED))
+			return -EFAULT;
+		/* 3. a fixed child has to sit on a fixed parent */
+		if (clk_is_readonly(clk) && !clk_is_readonly(clk->parent))
+			return -EFAULT;
+#endif
+		klist_add_tail(&clk->child_node, &clk->parent->childs);
+		clk->parent->nr_clocks++;
+	}
+
+	ret = kobject_add(&clk->kobj,
+		(clk->parent ? &clk->parent->kobj : clk_kobj), clk->name);
+	if (ret)
+		goto err_0;
+
+	clk->kdevices =	kobject_create_and_add("devices", &clk->kobj);
+	if (!clk->kdevices)
+		goto err_1;
+
+	klist_add_tail(&clk->node, &clk_list);
+	if (clk->flags & CLK_ALWAYS_ENABLED) {
+		__clk_enable(clk);
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+	}
+	return ret;
+
+err_1:
+	/* subsystem_remove_file... removed in the common code... ??? */
+	kobject_del(&clk->kobj);
+err_0:
+	return ret;
+}
+EXPORT_SYMBOL(clk_register);
+
+/**
+  * clk_unregister -
+  * unregisters the clock from system
+  */
+int clk_unregister(struct clk *clk)
+{
+	pr_debug("\n");
+
+	if (!clk)
+		return -EFAULT;
+
+	if (!list_empty(&clk->devices.k_list))
+		return -EFAULT; /* somebody is still using this clock */
+
+	kobject_del(clk->kdevices);
+	kfree(clk->kdevices);
+	/* subsystem_remove_file... removed in the common code... ??? */
+	kobject_del(&clk->kobj);
+	klist_del(&clk->node);
+	if (clk->parent) {
+		klist_del(&clk->child_node);
+		clk->parent->nr_clocks--;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(clk_unregister);
+
+static int clk_add_devinfo(struct pdev_clk_info *info)
+{
+	int ret = 0;
+	pr_debug("\n");
+
+#ifdef CLK_SAFE_CODE
+	if (!info || !info->clk || !check_clk(info->clk))
+		return -EFAULT;
+#endif
+	ret = sysfs_create_link(info->clk->kdevices, &info->pdev->dev.kobj,
+		dev_name(&info->pdev->dev));
+	if (ret) {
+		pr_debug(" Error %d\n", ret);
+		return ret;
+	}
+	klist_add_tail(&info->node, &info->clk->devices);
+
+	return 0;
+}
+
+static int clk_del_devinfo(struct pdev_clk_info *info)
+{
+	pr_debug("\n");
+
+#ifdef CLK_SAFE_CODE
+	if (!info || !info->clk || !check_clk(info->clk))
+		return -EFAULT;
+#endif
+	sysfs_remove_link(info->clk->kdevices, dev_name(&info->pdev->dev));
+	klist_del(&info->node);
+
+#ifndef CONFIG_PM_RUNTIME
+	/*
+	 * Without PM_RUNTIME the GCF assumes the device is
+	 * 'not active' when it's removed
+	 */
+	clk_notify_child_event(CHILD_DEVICE_DISABLED, info->clk);
+#endif
+	return 0;
+}
+
+int clk_probe_device(struct platform_device *dev, enum pdev_probe_state state)
+{
+	int idx;
+	switch (state) {
+	case PDEV_PROBEING:
+		/* before the .probe function is called the GCF
+		 * has to turn-on _all_ the clocks the device uses
+		 * to garantee a safe .probe
+		 */
+		for (idx = 0; idx < pdevice_num_clocks(dev); ++idx)
+			if (pdevice_clock(dev, idx))
+				clk_enable(pdevice_clock(dev, idx));
+		return 0;
+	case PDEV_PROBED:
+#ifdef CONFIG_PM_RUNTIME
+	/*
+	 * Here the GCF should check the device's pm_runtime state
+	 * And if the device is suspended the clk_frmwk can turn-off the clocks
+	 */
+#else
+	/*
+	 * Without PM_RUNTIME the GCF assumes the device is active
+	 */
+	for (idx = 0; idx < pdevice_num_clocks(dev); ++idx)
+		clk_notify_child_event(CHILD_DEVICE_ENABLED,
+			pdevice_clock(dev, idx));
+#endif
+	break;
+	case PDEV_PROBE_FAILED:
+	/*
+	 * TO DO something...
+	 */
+		break;
+	}
+	return 0;
+}
+
+int clk_add_device(struct platform_device *dev, enum pdev_add_state state)
+{
+	int idx;
+	int ret;
+
+	if (!dev)
+		return -EFAULT;
+
+	switch (state) {
+	case PDEV_ADDING:
+	case PDEV_ADD_FAILED:
+		/*
+		 * TO DO something
+		 */
+		return 0;
+	case PDEV_ADDED:
+		break;
+	}
+	/* case PDEV_ADDED ... */
+	if (!dev->clks || !pdevice_num_clocks(dev))
+		return 0;	/* this device will not use
+				   the clk framework */
+
+	pr_debug("%s.%d with %u clocks\n", dev->name, dev->id,
+		(unsigned int)pdevice_num_clocks(dev));
+
+	dev->clk_state = 0;
+	for (idx = 0; idx < pdevice_num_clocks(dev); ++idx) {
+		if (!pdevice_clock(dev, idx)) {	/* clk can not be NULL... */
+			pr_debug("Error clock NULL\n");
+			continue;
+		}
+		pr_debug("->under %s\n", dev->clks[idx].clk->name);
+		dev->clks[idx].pdev = dev;
+		ret = clk_add_devinfo(&dev->clks[idx]);
+		if (ret)
+			goto err_0;
+	}
+
+	return 0;
+err_0:
+	for (--idx; idx >= 0; --idx)
+		clk_del_devinfo(&dev->clks[idx]);
+
+	return -EINVAL;
+}
+
+int clk_del_device(struct platform_device *dev)
+{
+	int idx;
+	if (!dev)
+		return -EFAULT;
+
+	for (idx = 0; idx < pdevice_num_clocks(dev); ++idx)
+		clk_del_devinfo(&dev->clks[idx]);
+
+	return 0;
+}
+
+void clk_put(struct clk *clk)
+{
+	if (clk && !IS_ERR(clk))
+		kobject_put(&clk->kobj);
+}
+
+static int clk_is_parent(struct clk const *child, struct clk const *parent)
+{
+	if (!child || !parent)
+		return 0;
+	if (!child->parent)
+		return 0;
+	if (child->parent == parent)
+		return 1;
+	else
+		return clk_is_parent(child->parent, parent);
+}
+
+int clk_enable(struct clk *clk)
+{
+	int ret;
+	struct clk_tnode transaction;
+	struct clk_event event;
+
+	event = EVENT(clk, 0, CLK_UNDEFINED_RATE);
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	pr_debug("%s\n", clk->name);
+
+
+	if (clk->flags & CLK_ALWAYS_ENABLED || clk_is_enabled(clk))
+		return 0;
+
+	if (clk->parent) {
+		/* turn-on the parent if the parent is 'auto_switch' */
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+
+		if (!clk_is_enabled(clk->parent)) {
+			/* the parent is still disabled... */
+			clk_notify_child_event(CHILD_CLOCK_DISABLED,
+				clk->parent);
+			return -EINVAL;
+		}
+	}
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock update */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+		}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	ret = __clk_enable(clk);
+
+	event.new_rate = clk_get_rate(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	if (ret)
+		clk_notify_child_event(CHILD_CLOCK_DISABLED, clk->parent);
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+/**
+ * clk_disable -
+ * disables the clock
+ * Is isn't really good that it's a 'void' function...
+ * but this is common interface
+ */
+void clk_disable(struct clk *clk)
+{
+	struct clk_tnode transaction;
+	struct clk_event event;
+	int ret;
+
+	event = EVENT(clk, clk_get_rate(clk), 0);
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	pr_debug("\n");
+
+	if (clk->flags & CLK_ALWAYS_ENABLED || !clk_is_enabled(clk))
+		return;
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret)
+		goto err_0;
+
+	/* if not zero somebody doens't agree the clock update */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret)
+		goto err_1;
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	__clk_disable(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	clk_notify_child_event(CHILD_CLOCK_DISABLED, clk->parent);
+
+	return ;
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	int ret = -EOPNOTSUPP;
+	struct clk *old_parent = clk->parent;
+	struct clk_event event;
+	struct clk_tnode transaction;
+	int clk_was_enabled = clk_is_enabled(clk);
+
+	event = EVENT(clk, clk_get_rate(clk), CLK_UNDEFINED_RATE);
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	if (!clk || !parent)
+		return -EINVAL;
+
+	if (clk->parent == parent)
+		return 0;
+
+	pr_debug("\n");
+
+	if (clk_was_enabled && !clk_is_enabled(parent))
+		/* turn-on parent if possible */
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, parent);
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock updated */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+	}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	/* Now we updated the hw */
+	ret = __clk_set_parent(clk, parent);
+	if (ret) {
+		/* there was a problem...
+		 * therefore clk is still on the old parent
+		 */
+		clk->parent = old_parent; /* to be safe ! */
+		goto err_2;
+	}
+
+	klist_del(&clk->child_node);
+
+	clk->parent = parent;
+
+	ret = kobject_move(&clk->kobj, &clk->parent->kobj);
+	if (ret)
+		;
+
+	klist_add_tail(&clk->child_node, &clk->parent->childs);
+
+	clk->parent->nr_clocks++;
+	old_parent->nr_clocks--;
+
+err_2:
+	event.new_rate = clk_get_rate(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	if (clk_was_enabled && !ret) {
+		/* 5. to decrease the old_parent nchild counter */
+		clk_notify_child_event(CHILD_CLOCK_DISABLED, old_parent);
+		/* 5. increase the new_parent nchild counter */
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+		/* 6. to decrease the old_parent nchild counter */
+		clk_notify_child_event(CHILD_CLOCK_DISABLED, old_parent);
+		}
+
+	return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret = -EOPNOTSUPP;
+	struct clk_event event;
+	struct clk_tnode transaction;
+
+	event = EVENT(clk, clk_get_rate(clk), clk_round_rate(clk, rate));
+	transaction = TRANSACTION_ROOT(1, &event);
+
+	pr_debug("\n");
+
+	if (clk_is_readonly(clk))
+		/* read only clock doesn't have to be "touched" !!!! */
+		return -EPERM;
+
+	if (event.new_rate == clk_get_rate(clk))
+		return 0;
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock updated */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+	}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	__clk_set_rate(clk, event.new_rate);
+	/* reload new_rate to avoid hw rounding... */
+	event.new_rate = clk_get_rate(clk);
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	pr_debug("\n");
+
+	if (likely(clk->ops && clk->ops->round))
+		return clk->ops->round(clk, rate);
+	return rate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+unsigned long clk_evaluate_rate(struct clk *clk, unsigned long prate)
+{
+	pr_debug("\n");
+	if (!clk->parent)/* without parent this function has no meaning */
+		return CLK_UNDEFINED_RATE;
+
+	if (!prate)/* on parent disabled than disable the child */
+		return 0;
+
+	if (likely(clk->ops && clk->ops->eval))
+		return clk->ops->eval(clk, prate);
+
+	return CLK_UNDEFINED_RATE;
+}
+EXPORT_SYMBOL(clk_evaluate_rate);
+
+int clk_set_rates(struct clk **clks, unsigned long *rates, unsigned long nclks)
+{
+	int i, ret = 0;
+	struct clk_event *evt;
+	struct clk_tnode transaction = TRANSACTION_ROOT(nclks, NULL)
+
+	pr_debug("\n");
+
+	if (!clks || !rates || !nclks)
+		return -EINVAL;
+	evt = kmalloc(sizeof(*evt) *
+		tnode_get_size(&transaction), GFP_KERNEL);
+
+	if (!evt)
+		return -ENOMEM;
+
+	tnode_set_events(&transaction, evt);
+
+	for (i = 0; i < tnode_get_size(&transaction); ++i) {
+		tnode_set_clock(&transaction, i, clks[i]);
+		tnode_get_event(&transaction, i)->old_rate =
+			clk_get_rate(clks[i]);
+		tnode_get_event(&transaction, i)->new_rate =
+			clk_round_rate(clks[i], rates[i]);
+	}
+
+	ret = clk_trnsc_fsm(TRNSC_ENTER_CLOCK, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_0;
+	}
+
+	/* if not zero somebody doens't agree the clock updated */
+	ret = clk_trnsc_fsm(TRNSC_ENTER_DEVICE, &transaction);
+	if (ret) {
+		ret = -EPERM;
+		goto err_1;
+	}
+
+	clk_trnsc_fsm(TRNSC_PRE_DEVICE, &transaction);
+
+	for (i = 0; i < tnode_get_size(&transaction); ++i) {
+		if (!clk_is_enabled(clks[i]) && rates[i])
+			ret |= __clk_enable(clks[i]);
+		else if (clk_is_enabled(clks[i]) && !rates[i])
+			ret |= __clk_disable(clks[i]);
+		else
+			ret |= __clk_set_rate(clks[i], rates[i]);
+
+		/* reload new_rate to avoid hw rounding... */
+		tnode_get_event(&transaction, i)->new_rate =
+			clk_get_rate(clks[i]);
+	}
+
+	clk_trnsc_fsm(TRNSC_CHANGE_CLOCK, &transaction);
+
+	clk_trnsc_fsm(TRNSC_POST_DEVICE, &transaction);
+
+err_1:
+	clk_trnsc_fsm(TRNSC_EXIT_DEVICE, &transaction);
+
+err_0:
+	clk_trnsc_fsm(TRNSC_EXIT_CLOCK, &transaction);
+
+	kfree(evt);
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rates);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	struct clk *clk = NULL;
+	struct clk *clkp;
+	struct klist_iter i;
+	int found = 0, idno;
+
+	mutex_lock(&clk_list_sem);
+#if 0
+	if (dev == NULL || dev->bus != &platform_bus_type)
+		idno = -1;
+	else
+		idno = to_platform_device(dev)->id;
+
+	klist_iter_init(&clk_list, &i);
+	while ((clkp = next_clock(&i)) && !found)
+		if (clk->id == idno && strcmp(id, clk->name) == 0 &&
+			try_module_get(clk->owner)) {
+				clk = clkp;
+				found = 1;
+		}
+	klist_iter_exit(&i);
+
+	if (found)
+		goto _found;
+#endif
+	klist_iter_init(&clk_list, &i);
+	while ((clkp = next_clock(&i)))
+		if (strcmp(id, clkp->name) == 0
+		    && try_module_get(clkp->owner)) {
+			clk = clkp;
+			break;
+		}
+	klist_iter_exit(&i);
+_found:
+	mutex_unlock(&clk_list_sem);
+	return clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+int clk_for_each(int (*fn) (struct clk *clk, void *data), void *data)
+{
+	struct clk *clkp;
+	struct klist_iter i;
+	int result = 0;
+
+	if (!fn)
+		return -EFAULT;
+
+	pr_debug("\n");
+	mutex_lock(&clk_list_sem);
+	klist_iter_init(&clk_list, &i);
+
+	while ((clkp = next_clock(&i)))
+		result |= fn(clkp, data);
+
+	klist_iter_exit(&i);
+	mutex_unlock(&clk_list_sem);
+	return result;
+}
+EXPORT_SYMBOL(clk_for_each);
+
+int clk_for_each_child(struct clk *clk,
+	int (*fn) (struct clk *clk, void *data), void *data)
+{
+	struct clk *clkp;
+	struct klist_iter i;
+	int result = 0;
+
+	if (!clk || !fn)
+		return -EFAULT;
+
+	klist_iter_init(&clk->childs, &i);
+
+	while ((clkp = next_child_clock(&i)))
+		result |= fn(clkp, data);
+
+	klist_iter_exit(&i);
+
+	return result;
+}
+EXPORT_SYMBOL(clk_for_each_child);
+
+static int __init early_clk_complete(struct clk *clk, void *data)
+{
+	int ret;
+
+	ret = kobject_add(&clk->kobj,
+		(clk->parent ? &clk->parent->kobj : clk_kobj),
+		clk->name);
+	if (ret)
+		return ret;
+
+	clk->kdevices = kobject_create_and_add("devices", &clk->kobj);
+	if (!clk->kdevices)
+		return -EINVAL;
+
+	return 0;
+}
+
+int __init early_clk_register(struct clk *clk)
+{
+	int retval = 0;
+	if (!clk)
+		return -EFAULT;
+	pr_debug("%s\n", clk->name);
+
+	clk_initialize(clk);
+
+	/* Initialize ... */
+	__clk_init(clk);
+
+	if (clk->parent) {
+#ifdef CLK_SAFE_CODE
+		/* 1. the parent has to be registered */
+		if (!check_clk(clk->parent))
+			return -ENODEV;
+		/* 2. an always enabled child has to sit on a always
+		 *    enabled parent!
+		 */
+		if (clk->flags & CLK_ALWAYS_ENABLED &&
+			!(clk->parent->flags & CLK_ALWAYS_ENABLED))
+			return -EFAULT;
+		/* 3. a fixed child has to sit on a fixed parent */
+		if (clk_is_readonly(clk) && !clk_is_readonly(clk->parent))
+			return -EFAULT;
+#endif
+		klist_add_tail(&clk->child_node, &clk->parent->childs);
+		clk->parent->nr_clocks++;
+	}
+
+	klist_add_tail(&clk->node, &clk_list);
+	if (clk->flags & CLK_ALWAYS_ENABLED) {
+		__clk_enable(clk);
+		clk_notify_child_event(CHILD_CLOCK_ENABLED, clk->parent);
+	}
+	return retval;
+}
+
+int __init clock_init(void)
+{
+	clk_kobj = kobject_create_and_add("clocks", NULL);
+	if (!clk_kobj)
+		return -EINVAL ;
+
+	clk_for_each(early_clk_complete, NULL);
+
+	printk(KERN_INFO CLK_NAME " " CLK_VERSION "\n");
+
+	return 0;
+}
+
diff --git a/drivers/base/clk.h b/drivers/base/clk.h
new file mode 100644
index 0000000..61672ef
--- /dev/null
+++ b/drivers/base/clk.h
@@ -0,0 +1,319 @@
+/*
+   -------------------------------------------------------------------------
+   clk.h
+   -------------------------------------------------------------------------
+   (C) STMicroelectronics 2008
+   (C) STMicroelectronics 2009
+   Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+   ----------------------------------------------------------------------------
+   May be copied or modified under the terms of the GNU General Public
+   License v.2 ONLY.  See linux/COPYING for more information.
+
+   ------------------------------------------------------------------------- */
+
+#ifdef CONFIG_GENERIC_CLK_FM
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/klist.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <asm/atomic.h>
+
+enum clk_ops_id {
+	__CLK_INIT = 0,
+	__CLK_ENABLE,
+	__CLK_DISABLE,
+	__CLK_SET_RATE,
+	__CLK_SET_PARENT,
+	__CLK_RECALC,
+	__CLK_ROUND,
+	__CLK_EVAL,
+};
+
+extern struct klist clk_list;
+/**
+  * clk_tnode
+  *      it's the internal strucure used to track each node
+  *      in the transaction graph.
+  *      _NO_ api is showed to the other modules
+  */
+struct clk_tnode {
+	/** @tid: the tnode id */
+	unsigned long tid;
+	/** @size: how may clock are involved in this tnode */
+	unsigned long size;
+	/** @parent: the parent tnode */
+	struct clk_tnode *parent;
+	/* @events_map: a bitmap to declare the
+	 * valid events in this tnode
+	 */
+	unsigned long events_map;
+	/** @events: the event array of this tnode */
+	struct clk_event *events;
+	/** @child: links the childres tnode */
+	struct list_head childs;
+	/** @pnode: links the tnode to the parent */
+	struct list_head pnode;
+};
+
+/*
+ *  tnode_get_size -
+ *  returns the number of events in the transaction
+ */
+static inline unsigned long
+tnode_get_size(struct clk_tnode *tnode)
+{
+	return tnode->size;
+}
+
+static inline unsigned long
+tnode_get_map(struct clk_tnode *tnode)
+{
+	return tnode->events_map;
+}
+
+static inline unsigned long
+tnode_check_map_id(struct clk_tnode *node, int id)
+{
+	return node->events_map & (1 << id);
+}
+
+static inline void
+tnode_set_map_id(struct clk_tnode *node, int id)
+{
+	node->events_map |= (1 << id);
+}
+
+static inline unsigned long
+tnode_get_id(struct clk_tnode *node)
+{
+	return node->tid;
+}
+
+static inline struct clk_event*
+tnode_get_event(struct clk_tnode *node, int id)
+{
+	return &(node->events[id]);
+}
+
+static inline struct clk_event *tnode_get_events(struct clk_tnode *node)
+{
+	return tnode_get_event(node, 0);
+}
+
+static inline void
+tnode_set_events(struct clk_tnode *node, struct clk_event *events)
+{
+	node->events = events;
+}
+
+static inline struct clk*
+tnode_get_clock(struct clk_tnode *node, int id)
+{
+	return tnode_get_event(node, id)->clk;
+}
+
+static inline void
+tnode_set_clock(struct clk_tnode *node, int id, struct clk *clk)
+{
+	node->events[id].clk = clk;
+}
+
+static inline struct clk_tnode *tnode_get_parent(struct clk_tnode *node)
+{
+	return node->parent;
+}
+
+#define tnode_for_each_valid_events(node, _j)			\
+	for ((_j) = (ffs(tnode_get_map(node)) - 1);		\
+	     (_j) < tnode_get_size((node)); ++(_j))		\
+			if (tnode_check_map_id((node), (_j)))
+
+#define EVENT(_clk,  _oldrate, _newrate)		\
+	(struct clk_event)				\
+	{						\
+		.clk = (struct clk *)(_clk),		\
+		.old_rate = (unsigned long)(_oldrate),	\
+		.new_rate = (unsigned long)(_newrate),	\
+	};
+
+#define TRANSACTION_ROOT(_num, _event)					\
+	(struct clk_tnode) {						\
+		.tid    = atomic_inc_return(&transaction_counter),	\
+		.size   = (_num),					\
+		.events = (struct clk_event *)(_event),			\
+		.parent = NULL,						\
+		.childs = LIST_HEAD_INIT(transaction.childs),		\
+		.events_map = 0,					\
+		};
+
+#define klist_function_support(_name, _type, _field, _kobj)		\
+static void klist_get_##_name(struct klist_node *n)			\
+{									\
+	struct _type *entry = container_of(n, struct _type, _field);	\
+	kobject_get(&entry->_kobj);					\
+}									\
+static void klist_put_##_name(struct klist_node *n)			\
+{									\
+	struct _type *entry = container_of(n, struct _type, _field);	\
+	kobject_put(&entry->_kobj);					\
+}
+
+#define klist_entry_support(name, type, field)				\
+static struct type *next_##name(struct klist_iter *i)			\
+{	struct klist_node *n = klist_next(i);				\
+	return n ? container_of(n, struct type, field) : NULL;		\
+}
+
+static inline void
+clk_event_init(struct clk_event *evt, struct clk *clk,
+		unsigned long oldrate, unsigned long newrate)
+{
+	evt->clk      = clk;
+	evt->old_rate = oldrate;
+	evt->new_rate = newrate;
+}
+
+enum clk_fsm_e {
+	TRNSC_ENTER_CLOCK	= 0x10,
+	TRNSC_ENTER_DEVICE	= NOTIFY_CLK_ENTERCHANGE,	/* 0x1 */
+	TRNSC_PRE_DEVICE	= NOTIFY_CLK_PRECHANGE,		/* 0x2 */
+	TRNSC_CHANGE_CLOCK	= 0x20,
+	TRNSC_POST_DEVICE	= NOTIFY_CLK_POSTCHANGE,	/* 0x4 */
+	TRNSC_EXIT_DEVICE	= NOTIFY_CLK_EXITCHANGE,	/* 0x8 */
+	TRNSC_EXIT_CLOCK	= 0x40
+};
+
+#define DEV_SUSPENDED_ON_TRANSACTION	(0x10)
+#define DEV_RESUMED_ON_TRANSACTION	(0x20)
+#define DEV_ON_TRANSACTION	(TRNSC_ENTER_DEVICE	|	\
+				TRNSC_PRE_DEVICE	|	\
+				TRNSC_POST_DEVICE	|	\
+				TRNSC_EXIT_DEVICE)
+
+static inline int
+pdev_transaction_move_on(struct platform_device *dev, unsigned int value)
+{
+	int ret = -EINVAL;
+	unsigned long flag;
+#ifdef CONFIG_CLK_DEBUG
+	static const char *dev_state[] = {
+		"dev_enter",
+		"dev_pre",
+		"dev_post",
+		"dev_exit"
+	};
+
+	unsigned long old = dev->clk_state & DEV_ON_TRANSACTION;
+	int was = 0, is = 0;
+	if (
+	   (old == 0 && value == TRNSC_ENTER_DEVICE) ||
+	   (old == TRNSC_ENTER_DEVICE && value == TRNSC_EXIT_DEVICE) ||
+	   (old == TRNSC_ENTER_DEVICE && value == TRNSC_PRE_DEVICE) ||
+	   (old == TRNSC_PRE_DEVICE && value == TRNSC_POST_DEVICE) ||
+	   (old == TRNSC_POST_DEVICE && value == TRNSC_EXIT_DEVICE))
+		goto ok;
+	switch (old) {
+	case TRNSC_ENTER_DEVICE:
+		was = 0;
+		break;
+	case TRNSC_PRE_DEVICE:
+		was = 1;
+		break;
+	case TRNSC_POST_DEVICE:
+		was = 2;
+		break;
+	case TRNSC_EXIT_DEVICE:
+		was = 3;
+		break;
+	}
+	switch (value) {
+	case TRNSC_ENTER_DEVICE:
+		is = 0;
+		break;
+	case TRNSC_PRE_DEVICE:
+		is = 1;
+		break;
+	case TRNSC_POST_DEVICE:
+		is = 2;
+		break;
+	case TRNSC_EXIT_DEVICE:
+		is = 3;
+		break;
+	}
+	printk(KERN_ERR "The device %s.%d shows a wrong evolution during "
+		"a clock transaction\nDev state was %s and moved on %s\n",
+		dev->name, dev->id, dev_state[was], dev_state[is]);
+ok:
+#endif
+	local_irq_save(flag);
+	if ((dev->clk_state & DEV_ON_TRANSACTION) != value) {
+		dev->clk_state &= ~DEV_ON_TRANSACTION;
+		dev->clk_state |= value;
+		ret = 0;
+	}
+	local_irq_restore(flag);
+	return ret;
+}
+
+static inline int
+clk_set_towner(struct clk *clk, struct clk_tnode *node)
+{
+	return atomic_cmpxchg((atomic_t *)&clk->towner, 0, (int)node);
+}
+
+static inline void
+clk_clean_towner(struct clk *clk)
+{
+	atomic_set((atomic_t *)(&clk->towner), 0);
+}
+
+static inline int
+clk_is_enabled(struct clk *clk)
+{
+	return clk->rate != 0;
+}
+
+static inline int
+clk_is_readonly(struct clk *clk)
+{
+	return !clk->ops || !clk->ops->set_rate;
+}
+
+static inline int
+clk_allow_propagation(struct clk *clk)
+{
+	return !!(clk->flags & CLK_EVENT_PROPAGATES);
+}
+
+static inline int
+clk_is_auto_switching(struct clk *clk)
+{
+	return !!(clk->flags & CLK_AUTO_SWITCHING);
+}
+
+static inline int
+clk_follow_parent(struct clk *clk)
+{
+	return !!(clk->flags & CLK_FOLLOW_PARENT);
+}
+
+enum pdev_add_state {
+	PDEV_ADDING,
+	PDEV_ADDED,
+	PDEV_ADD_FAILED,
+};
+
+enum pdev_probe_state {
+	PDEV_PROBEING,
+	PDEV_PROBED,
+	PDEV_PROBE_FAILED,
+};
+
+int clk_add_device(struct platform_device *dev, enum pdev_add_state state);
+int clk_probe_device(struct platform_device *dev, enum pdev_probe_state state);
+int clk_del_device(struct platform_device *dev);
+
+#endif
diff --git a/drivers/base/clk_pm.c b/drivers/base/clk_pm.c
new file mode 100644
index 0000000..56c1760
--- /dev/null
+++ b/drivers/base/clk_pm.c
@@ -0,0 +1,197 @@
+/*
+ * -------------------------------------------------------------------------
+ * clk_pm.c
+ * -------------------------------------------------------------------------
+ * (C) STMicroelectronics 2008
+ * (C) STMicroelectronics 2009
+ * Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+ * -------------------------------------------------------------------------
+ * May be copied or modified under the terms of the GNU General Public
+ * License v.2 ONLY.  See linux/COPYING for more information.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <linux/clk.h>
+#include <linux/klist.h>
+#include <linux/list.h>
+#include <linux/sysdev.h>
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include "power/power.h"
+#include "clk.h"
+#include "base.h"
+
+static int
+__clk_operations(struct clk *clk, unsigned long rate, enum clk_ops_id id_ops)
+{
+	int ret = -EINVAL;
+	unsigned long *ops_fns = (unsigned long *)clk->ops;
+	if (likely(ops_fns && ops_fns[id_ops])) {
+		int (*fns)(struct clk *clk, unsigned long rate)
+			= (void *)ops_fns[id_ops];
+		unsigned long flags;
+		spin_lock_irqsave(&clk->lock, flags);
+		ret = fns(clk, rate);
+		spin_unlock_irqrestore(&clk->lock, flags);
+	}
+	return ret;
+}
+
+static inline int __clk_init(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_INIT);
+}
+
+static inline int __clk_enable(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_ENABLE);
+}
+
+static inline int __clk_disable(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_DISABLE);
+}
+
+static inline int __clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	return __clk_operations(clk, rate, __CLK_SET_RATE);
+}
+
+static inline int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	return __clk_operations(clk, (unsigned long)parent, __CLK_SET_PARENT);
+}
+
+static inline int __clk_recalc_rate(struct clk *clk)
+{
+	return __clk_operations(clk, 0, __CLK_RECALC);
+}
+
+static inline int pm_clk_ratio(struct clk *clk)
+{
+	register unsigned int val, exp;
+
+	val = ((clk->flags >> CLK_PM_RATIO_SHIFT) &
+		((1 << CLK_PM_RATIO_NRBITS) - 1)) + 1;
+	exp = ((clk->flags >> CLK_PM_EXP_SHIFT) &
+		((1 << CLK_PM_EXP_NRBITS) - 1));
+
+	return val << exp;
+}
+
+static inline int pm_clk_is_off(struct clk *clk)
+{
+	return ((clk->flags & CLK_PM_TURNOFF) == CLK_PM_TURNOFF);
+}
+
+static inline void pm_clk_set(struct clk *clk, int edited)
+{
+#define CLK_PM_EDITED (1 << CLK_PM_EDIT_SHIFT)
+	clk->flags &= ~CLK_PM_EDITED;
+	clk->flags |= (edited ? CLK_PM_EDITED : 0);
+}
+
+static inline int pm_clk_is_modified(struct clk *clk)
+{
+	return ((clk->flags & CLK_PM_EDITED) != 0);
+}
+
+static int clk_resume_from_standby(struct clk *clk, void *data)
+{
+	pr_debug("\n");
+	if (!likely(clk->ops))
+		return 0;
+	/* check if the pm modified the clock */
+	if (!pm_clk_is_modified(clk))
+		return 0;;
+	pm_clk_set(clk, 0);
+	if (pm_clk_is_off(clk))
+		__clk_enable(clk);
+	else
+		__clk_set_rate(clk, clk->rate * pm_clk_ratio(clk));
+	return 0;
+}
+
+static int clk_on_standby(struct clk *clk, void *data)
+{
+	pr_debug("\n");
+
+	if (!clk->ops)
+		return 0;
+	if (!clk->rate) /* already disabled */
+		return 0;
+
+	pm_clk_set(clk, 1);	/* set as modified */
+	if (pm_clk_is_off(clk))		/* turn-off */
+		__clk_disable(clk);
+	else    /* reduce */
+		__clk_set_rate(clk, clk->rate / pm_clk_ratio(clk));
+	return 0;
+}
+
+static int clk_resume_from_hibernation(struct clk *clk, void *data)
+{
+	unsigned long rate = clk->rate;
+	pr_debug("\n");
+	__clk_set_parent(clk, clk->parent);
+	__clk_set_rate(clk, rate);
+	__clk_recalc_rate(clk);
+	return 0;
+}
+
+static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+{
+	static pm_message_t prev_state;
+
+	switch (state.event) {
+	case PM_EVENT_ON:
+		switch (prev_state.event) {
+		case PM_EVENT_FREEZE: /* Resumeing from hibernation */
+			clk_for_each(clk_resume_from_hibernation, NULL);
+			break;
+		case PM_EVENT_SUSPEND:
+			clk_for_each(clk_resume_from_standby, NULL);
+			break;
+		}
+	case PM_EVENT_SUSPEND:
+		clk_for_each(clk_on_standby, NULL);
+		break;
+	case PM_EVENT_FREEZE:
+		break;
+	}
+	prev_state = state;
+	return 0;
+}
+
+static int clks_sysdev_resume(struct sys_device *dev)
+{
+	return clks_sysdev_suspend(dev, PMSG_ON);
+}
+
+static struct sysdev_class clk_sysdev_class = {
+	.name = "clks",
+};
+
+static struct sysdev_driver clks_sysdev_driver = {
+	.suspend = clks_sysdev_suspend,
+	.resume = clks_sysdev_resume,
+};
+
+static struct sys_device clks_sysdev_dev = {
+	.cls = &clk_sysdev_class,
+};
+
+static int __init clk_sysdev_init(void)
+{
+	sysdev_class_register(&clk_sysdev_class);
+	sysdev_driver_register(&clk_sysdev_class, &clks_sysdev_driver);
+	sysdev_register(&clks_sysdev_dev);
+	return 0;
+}
+
+subsys_initcall(clk_sysdev_init);
diff --git a/drivers/base/clk_utils.c b/drivers/base/clk_utils.c
new file mode 100644
index 0000000..a222aa7
--- /dev/null
+++ b/drivers/base/clk_utils.c
@@ -0,0 +1,456 @@
+/*
+ * -------------------------------------------------------------------------
+ * clk_utils.c
+ * -------------------------------------------------------------------------
+ * (C) STMicroelectronics 2008
+ * (C) STMicroelectronics 2009
+ * Author: Francesco M. Virlinzi <francesco.virlinzi@st.com>
+ * -------------------------------------------------------------------------
+ * May be copied or modified under the terms of the GNU General Public
+ * License v.2 ONLY.  See linux/COPYING for more information.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/klist.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/sysdev.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include "power/power.h"
+#include "clk.h"
+#include "base.h"
+
+int clk_generic_notify(unsigned long code,
+	struct platform_device *pdev, void *data)
+{
+	struct clk_event *event = (struct clk_event *)data;
+	unsigned long event_decode = clk_event_decode(event);
+
+	switch (code) {
+	case NOTIFY_CLK_ENTERCHANGE:
+		return NOTIFY_EVENT_HANDLED;	/* to accept */
+
+	case NOTIFY_CLK_PRECHANGE:
+		/* without clock (not still enabled) the device can not work */
+		if (event_decode == _CLK_ENABLE)
+			return NOTIFY_EVENT_NOTHANDLED;
+		return NOTIFY_EVENT_HANDLED;	/* to suspend */
+
+	case NOTIFY_CLK_POSTCHANGE:
+		/* without clock (just disabled) the device can not work */
+		if (event_decode == _CLK_DISABLE)
+			return NOTIFY_EVENT_NOTHANDLED;
+		return NOTIFY_EVENT_HANDLED;	/* to resume */
+
+	case NOTIFY_CLK_EXITCHANGE:
+		return NOTIFY_EVENT_HANDLED;
+	}
+
+	return NOTIFY_EVENT_HANDLED;
+}
+EXPORT_SYMBOL(clk_generic_notify);
+
+unsigned long clk_generic_evaluate_rate(struct clk *clk, unsigned long prate)
+{
+	unsigned long current_prate;
+
+	if (!clk->parent)
+		return -EINVAL;
+
+	if (!prate)	/* if zero return zero (on disable: disable!) */
+		return 0;
+
+	if (prate == CLK_UNDEFINED_RATE) /* on undefined: undefined */
+		return CLK_UNDEFINED_RATE;
+
+	current_prate = clk_get_rate(clk->parent);
+	if (current_prate == prate)
+		return clk_get_rate(clk);
+
+	if (current_prate > prate) /* down scale */
+		return (clk_get_rate(clk) * prate) / current_prate;
+	else
+		return (clk_get_rate(clk) / current_prate) * prate;
+}
+EXPORT_SYMBOL(clk_generic_evaluate_rate);
+
+#ifdef CONFIG_PROC_FS
+/*
+ * The "clocks" file is created under /proc
+ * to list all the clocks registered in the system
+ */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+static void *clk_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	struct list_head *tmp;
+	union {
+		loff_t value;
+		long parts[2];
+	} ltmp;
+
+	ltmp.value = *pos;
+	tmp = (struct list_head *)ltmp.parts[0];
+	tmp = tmp->next;
+	ltmp.parts[0] = (long)tmp;
+
+	*pos = ltmp.value;
+
+	if (tmp == &clk_list.k_list)
+		return NULL; /* No more to read */
+
+	return pos;
+}
+
+static void *clk_seq_start(struct seq_file *s, loff_t *pos)
+{
+	if (!*pos) { /* first call! */
+		union {
+			loff_t value;
+			long parts[2];
+		} ltmp;
+		ltmp.parts[0] = (long) clk_list.k_list.next;
+		*pos = ltmp. value;
+		return pos;
+	}
+	--(*pos); /* to realign *pos value! */
+
+	return clk_seq_next(s, NULL, pos);
+}
+
+static int clk_seq_show(struct seq_file *s, void *v)
+{
+	unsigned long *l = (unsigned long *)v;
+	struct list_head *node = (struct list_head *)(*l);
+	struct clk *clk = container_of(node, struct clk, node.n_node);
+	unsigned long rate = clk_get_rate(clk);
+
+	if (unlikely(!rate && !clk->parent))
+		return 0;
+
+	seq_printf(s, "%-12s\t: %ld.%02ldMHz - ", clk->name,
+	       rate / 1000000, (rate % 1000000) / 10000);
+	seq_printf(s, "[0x%p]", clk);
+	if (clk_is_enabled(clk))
+		seq_printf(s, " - enabled");
+
+	if (clk->parent)
+		seq_printf(s, " - [%s]", clk->parent->name);
+	seq_printf(s, "\n");
+
+	return 0;
+}
+
+static void clk_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static const struct seq_operations clk_seq_ops = {
+	.start = clk_seq_start,
+	.next = clk_seq_next,
+	.stop = clk_seq_stop,
+	.show = clk_seq_show,
+};
+
+static int clk_proc_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &clk_seq_ops);
+}
+
+static const struct file_operations clk_proc_ops = {
+	.owner = THIS_MODULE,
+	.open = clk_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static int __init clk_proc_init(void)
+{
+	struct proc_dir_entry *p;
+
+	p = create_proc_entry("clocks", S_IRUGO, NULL);
+
+	if (unlikely(!p))
+		return -EINVAL;
+
+	p->proc_fops = &clk_proc_ops;
+
+	return 0;
+}
+
+subsys_initcall(clk_proc_init);
+#endif
+
+#ifdef CONFIG_SYSFS
+static ssize_t clk_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+
+	return sprintf(buf, "%u\n", (unsigned int)clk_get_rate(clk));
+}
+
+static ssize_t clk_rate_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	unsigned long rate = simple_strtoul(buf, NULL, 10);
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+
+	if (rate) {
+		if (!clk_is_enabled(clk))
+			clk_enable(clk);
+		if (clk_set_rate(clk, rate) < 0)
+			return -EINVAL;
+	} else
+		clk_disable(clk);
+	return count;
+}
+
+static const char *clk_ctrl_token[] = {
+	"auto_switching",
+	"no_auto_switching",
+	"allow_propagation",
+	"no_allow_propagation",
+	"follow_parent",
+	"no_follow_parent",
+};
+static ssize_t clk_state_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+	ssize_t ret;
+
+
+	ret = sprintf(buf, "clock name: %s\n", clk->name);
+	if (clk_is_enabled(clk))
+		ret += sprintf(buf + ret, " + enabled\n");
+	else
+		ret += sprintf(buf + ret, " + disabled\n");
+	if (clk_is_readonly(clk))
+		ret += sprintf(buf + ret, " + rate read only\n");
+	else
+		ret += sprintf(buf + ret, " + rate writable\n");
+	ret +=
+	    sprintf(buf + ret, " + %s\n",
+		    clk_ctrl_token[(clk_allow_propagation(clk) ? 2 : 3)]);
+	ret +=
+	    sprintf(buf + ret, " + %s\n",
+		    clk_ctrl_token[(clk_is_auto_switching(clk) ? 0 : 1)]);
+	ret +=
+	    sprintf(buf + ret, " + %s\n",
+		    clk_ctrl_token[(clk_follow_parent(clk) ? 4 : 5)]);
+	ret +=
+	    sprintf(buf + ret, " + nr_clocks:  %u\n", clk->nr_clocks);
+	ret +=
+	    sprintf(buf + ret, " + nr_active_clocks:  %u\n",
+		clk->nr_active_clocks);
+	ret +=
+	    sprintf(buf + ret, " + nr_active_devices:  %u\n",
+		clk->nr_active_devices);
+	ret +=
+	    sprintf(buf + ret, " + rate: %u\n",
+		    (unsigned int)clk_get_rate(clk));
+	return ret;
+}
+
+static ssize_t clk_ctrl_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int idx, ret = 0;
+
+	ret += sprintf(buf + ret, "Allowed command:\n");
+
+	for (idx = 0; idx < ARRAY_SIZE(clk_ctrl_token); ++idx)
+		ret += sprintf(buf + ret, " + %s\n", clk_ctrl_token[idx]);
+
+	return ret;
+}
+static ssize_t clk_ctrl_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int i, idx_token, ret = -EINVAL;
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+
+	if (!count)
+		return ret;
+
+	for (i = 0, idx_token = -1; i < ARRAY_SIZE(clk_ctrl_token); ++i)
+		if (!strcmp(buf, clk_ctrl_token[i]))
+			idx_token = i;
+
+	if (idx_token == -EINVAL)
+		return ret;     /* token not valid... */
+
+	switch (idx_token) {
+	case 0:
+		clk->flags |= CLK_EVENT_PROPAGATES;
+		break;
+	case 1:
+		clk->flags &= ~CLK_EVENT_PROPAGATES;
+		break;
+	case 2:
+		clk->flags |= CLK_AUTO_SWITCHING;
+		if (!clk->nr_active_clocks && !clk->nr_active_devices)
+			clk_disable(clk);
+		else if (clk->nr_active_clocks || clk->nr_active_devices)
+			clk_enable(clk);
+		break;
+	case 3:
+		clk->flags &= ~CLK_AUTO_SWITCHING;
+		break;
+	case 4:
+		clk->flags |= CLK_FOLLOW_PARENT;
+		break;
+	case 5:
+		clk->flags &= ~CLK_FOLLOW_PARENT;
+		break;
+	}
+
+	return count;
+}
+
+static ssize_t clk_parent_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct clk *clk = container_of(kobj, struct clk, kobj);
+	struct clk *parent = clk_get(NULL, buf);
+
+	if (!parent)
+		return -EINVAL;
+
+	clk_put(parent);
+	clk_set_parent(clk, parent);
+
+	return count;
+}
+
+static struct kobj_attribute attributes[] = {
+__ATTR(state, S_IRUSR, clk_state_show, NULL),
+__ATTR(rate, S_IRUSR | S_IWUSR, clk_rate_show, clk_rate_store),
+__ATTR(control, S_IRUSR | S_IWUSR, clk_ctrl_show, clk_ctrl_store),
+__ATTR(parent, S_IWUSR, NULL, clk_parent_store)
+};
+
+static struct attribute *clk_attrs[] = {
+	&attributes[0].attr,
+	&attributes[1].attr,
+	&attributes[2].attr,
+	&attributes[3].attr,
+	NULL
+};
+
+static struct attribute_group clk_attr_group = {
+	.attrs = clk_attrs,
+	.name = "attributes"
+};
+
+#if 0
+static inline char *_strsep(char **s, const char *d)
+{
+	int i, len = strlen(d);
+retry:
+	if (!(*s) || !(**s))
+		return NULL;
+	for (i = 0; i < len; ++i) {
+		if (**s != *(d+i))
+			continue;
+		++(*s);
+		goto retry;
+	}
+	return strsep(s, d);
+}
+
+/**
+ * clk_rates_store
+ *
+ * It parses the buf to create multi clocks transaction
+ * via user space
+ * The buffer has to be something like:
+ * clock_A @ rate_A; clock_B @ rate_b; clock_C @ rate_c
+ */
+static ssize_t clk_rates_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int i, ret;
+	int nclock = 0;
+	unsigned long *rates;
+	struct clk **clocks;
+
+	if (!buf)
+		return -1;
+
+	for (i = 0; i < count; ++i)
+		if (buf[i] == '@')
+			++nclock;
+
+	rates = kmalloc(sizeof(long) * nclock, GFP_KERNEL);
+	if (!rates)
+		return -ENOMEM;
+
+	clocks = kmalloc(sizeof(void *) * nclock, GFP_KERNEL);
+	if (!clocks) {
+		ret = -ENOMEM;
+		goto err_0;
+	}
+
+	/* Parse the buffer */
+	for (i = 0; i < nclock; ++i) {
+		char *name;
+		char *nrate;
+		name  = _strsep((char **)&buf, "@ "); ++buf;
+		nrate = _strsep((char **)&buf, " ;"); ++buf;
+		if (!name || !nrate) {
+			ret = -EINVAL;
+			goto err_1;
+			}
+		clocks[i] = clk_get(NULL, name);
+		rates[i]  = simple_strtoul(nrate, NULL, 10);
+		if (!clocks[i]) { /* the clock doesn't exist! */
+			ret = -EINVAL;
+			goto err_1;
+			}
+	}
+
+	ret = clk_set_rates(clocks, rates, nclock);
+	if (ret >= 0)
+		ret = count; /* to say OK */
+
+err_1:
+	kfree(clocks);
+err_0:
+	kfree(rates);
+	return ret;
+}
+
+static struct kobj_attribute clk_rates_attr =
+	__ATTR(rates, S_IWUSR, NULL, clk_rates_store);
+#endif
+
+static int __init clk_add_attributes(struct clk *clk, void *data)
+{
+	int ret;
+
+	ret = sysfs_update_group(&clk->kobj, &clk_attr_group);
+
+	return ret;
+}
+
+static int __init clk_late_init(void)
+{
+	int ret;
+
+	ret = clk_for_each(clk_add_attributes, NULL);
+
+	return ret;
+}
+
+late_initcall(clk_late_init);
+#endif
diff --git a/drivers/base/init.c b/drivers/base/init.c
index 7bd9b6a..2441b26 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -24,6 +24,7 @@ void __init driver_init(void)
 	buses_init();
 	classes_init();
 	firmware_init();
+	clock_init();
 	hypervisor_init();
 
 	/* These are also core pieces, but must come after the
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 8b4708e..550d993 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -17,6 +17,8 @@
 #include <linux/bootmem.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
+#include "clk.h"
 
 #include "base.h"
 
@@ -272,9 +274,20 @@ int platform_device_add(struct platform_device *pdev)
 	pr_debug("Registering platform device '%s'. Parent at %s\n",
 		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
 
+#ifdef CONFIG_GENERIC_CLK_FM
+	clk_add_device(pdev, PDEV_ADDING);
+
+	ret = device_add(&pdev->dev);
+
+	clk_add_device(pdev, (ret ? PDEV_ADD_FAILED : PDEV_ADDED));
+
+	if (ret == 0)
+		return ret;
+#else
 	ret = device_add(&pdev->dev);
 	if (ret == 0)
 		return ret;
+#endif
 
  failed:
 	while (--i >= 0) {
@@ -311,6 +324,9 @@ void platform_device_del(struct platform_device *pdev)
 			if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
 				release_resource(r);
 		}
+#ifdef CONFIG_GENERIC_CLK_FM
+	clk_del_device(pdev);
+#endif
 	}
 }
 EXPORT_SYMBOL_GPL(platform_device_del);
@@ -445,7 +461,18 @@ static int platform_drv_probe(struct device *_dev)
 	struct platform_driver *drv = to_platform_driver(_dev->driver);
 	struct platform_device *dev = to_platform_device(_dev);
 
+#ifdef CONFIG_GENERIC_CLK_FM
+	int ret;
+	ret = clk_probe_device(dev, PDEV_PROBEING);
+	if (ret)
+		return ret;
+	ret = drv->probe(dev);
+
+	clk_probe_device(dev, (ret ? PDEV_PROBE_FAILED : PDEV_PROBED));
+	return ret;
+#else
 	return drv->probe(dev);
+#endif
 }
 
 static int platform_drv_probe_fail(struct device *_dev)
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 1db9bbf..e537bcd 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -12,6 +12,7 @@
 #define __LINUX_CLK_H
 
 struct device;
+struct platform_device;
 
 /*
  * The base API.
@@ -142,4 +143,254 @@ struct clk *clk_get_parent(struct clk *clk);
  */
 struct clk *clk_get_sys(const char *dev_id, const char *con_id);
 
+/**
+ * clk_set_rates - set the clock rates
+ * @clk: clocks source
+ * @rate: desired clock rates in Hz
+ * @nclks: the number of clocks
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_rates(struct clk **clk, unsigned long *rates, unsigned long nclks);
+
+#ifndef CONFIG_GENERIC_CLK_FM
+
+#define bind_clock(_clk)
+#define pdevice_setclock(_dev, _clk)
+#define pdevice_setclock_byname(_dev, _clkname)
+#define pdevice_num_clocks(_dev)
+#define pdevice_clock(dev, idx)
+
+#else
+
+#include <linux/kobject.h>
+#include <linux/klist.h>
+#include <linux/notifier.h>
+#include <linux/pm.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+
+/**
+ * Clock operation -
+ *
+ * It's a set of function pointer to identify all the capability on a clock
+ */
+struct clk_ops {
+/** @init initializes the clock	*/
+	int (*init)(struct clk *);
+/** @enable enables the clock	*/
+	int (*enable)(struct clk *);
+/** @disable disables the clock	*/
+	int (*disable)(struct clk *);
+/** @set_rate sets the new frequency rate */
+	int (*set_rate)(struct clk *, unsigned long value);
+/** @set_parent sets the new parent clock */
+	int (*set_parent)(struct clk *clk, struct clk *parent);
+/** @recalc updates the clock rate when the parent clock is updated	 */
+	void (*recalc)(struct clk *);
+/** @round returns the allowed rate on the required value	*/
+	unsigned long (*round)(struct clk *, unsigned long value);
+/** @eval evaluates the clock rate based on a parent_rate but the
+ * real clock rate is __not__ changed
+ */
+	unsigned long (*eval)(struct clk *, unsigned long parent_rate);
+};
+
+/**
+ * struct clk - clock object
+ */
+struct clk {
+	spinlock_t		lock;
+
+	struct kobject		kobj;
+	struct kobject		*kdevices;
+
+	int			id;
+
+	const char		*name;
+	struct module		*owner;
+
+	struct clk		*parent;
+	struct clk_ops		*ops;
+
+	void			*private_data;
+
+	unsigned long		rate;
+	unsigned long		flags;
+
+	unsigned int		nr_active_clocks;
+	unsigned int		nr_active_devices;
+	unsigned int		nr_clocks;
+
+	void			*towner;/* the transaction owner of the clock */
+
+	struct klist		childs;
+	struct klist		devices;
+
+	struct klist_node	node;		/* for global link	*/
+	struct klist_node	child_node;	/* for child link	*/
+};
+
+#define CLK_ALWAYS_ENABLED		(0x1 << 0)
+#define CLK_EVENT_PROPAGATES		(0x1 << 1)
+#define CLK_RATE_PROPAGATES		CLK_EVENT_PROPAGATES
+/* CLK_AUTO_SWITCHING: enable/disable the clock based on the
+ * current active children
+ */
+#define CLK_AUTO_SWITCHING		(0x1 << 2)
+/* CLK_FOLLOW_PARENT: enable/disable the clock as the parent is
+ * enabled/disabled
+ */
+#define CLK_FOLLOW_PARENT		(0x1 << 3)
+
+/*
+ * Flags to support the system standby
+ */
+#define CLK_PM_EXP_SHIFT	(24)
+#define CLK_PM_EXP_NRBITS	(7)
+#define CLK_PM_RATIO_SHIFT	(16)
+#define CLK_PM_RATIO_NRBITS	(8)
+#define CLK_PM_EDIT_SHIFT	(31)
+#define CLK_PM_EDIT_NRBITS	(1)
+#define CLK_PM_TURNOFF		(((1<<CLK_PM_EXP_NRBITS)-1) << CLK_PM_EXP_SHIFT)
+
+int early_clk_register(struct clk *);
+/**
+ * Registers a new clock into the system
+ */
+int clk_register(struct clk *);
+/**
+ * Unregisters a clock into the system
+ */
+int clk_unregister(struct clk *);
+
+/**
+ * Returns the clock rate if the  parent clock is 'parent_rate'
+ */
+unsigned long clk_evaluate_rate(struct clk *, unsigned long parent_rate);
+
+#define CLK_UNDEFINED_RATE	(-1UL)
+/**
+ * Utility functions in the clock framework
+ */
+int clk_for_each(int (*fn)(struct clk *, void *), void *);
+
+int clk_for_each_child(struct clk *, int (*fn)(struct clk *, void *), void *);
+
+/** struct pdev_clk_info -
+ *
+ *  It's a meta data used to link the device of linux driver model
+ *  to the clock framework.
+ *  The device driver developers has to set only the clk field
+ *  all the other fileds are managed in the clk core code
+ */
+struct pdev_clk_info {
+	/** the device owner    */
+	struct platform_device  *pdev;
+	/** the clock address	*/
+	struct clk		*clk;
+	/** used by the clock core*/
+	struct klist_node	node;
+};
+
+/******************** clk transition notifiers *******************/
+#define	NOTIFY_CLK_ENTERCHANGE	0x1
+#define	NOTIFY_CLK_PRECHANGE	0x2
+#define	NOTIFY_CLK_POSTCHANGE	0x4
+#define NOTIFY_CLK_EXITCHANGE	0x8
+
+/** struct clk_event
+ *
+ * It's the object propagated during a clock transaction.
+ * During a transaction each device will receive an array of 'struct clk_event'
+ * based on the clocks it uses
+ */
+struct clk_event {
+	/** on which clock the event is		*/
+	struct clk *clk;
+	/** the clock rate before the event	*/
+	unsigned long old_rate;
+	/** the clock rate after the event	*/
+	unsigned long new_rate;
+};
+
+enum clk_event_e {
+	_CLK_NOCHANGE,
+	_CLK_ENABLE,
+	_CLK_DISABLE,
+	_CLK_CHANGE
+};
+
+/**
+ * clk_event_decode -
+ *
+ * @event: the events has to be decoded
+ * It's an utility function to identify what each clock
+ * is doing
+ */
+static inline enum clk_event_e clk_event_decode(struct clk_event const *event)
+{
+	if (event->old_rate == event->new_rate)
+		return _CLK_NOCHANGE;
+	if (!event->old_rate && event->new_rate)
+		return _CLK_ENABLE;
+	if (event->old_rate && !event->new_rate)
+		return _CLK_DISABLE;
+	return _CLK_CHANGE;
+}
+
+enum notify_ret_e {
+	NOTIFY_EVENT_HANDLED = 0,		/* event handled	*/
+	NOTIFY_EVENT_NOTHANDLED,		/* event not handled	*/
+};
+
+/* Some macro device oriented static initialization */
+#define bind_clock(_clk)					\
+	.nr_clks = 1,						\
+	.clks = (struct pdev_clk_info[]) { {			\
+		.clk = (_clk),					\
+		} },
+
+#define pdevice_setclock(_dev, _clk)				\
+	(_dev)->clks[0].clk = (_clk);				\
+	(_dev)->nr_clks = 1;
+
+#define pdevice_setclock_byname(_dev, _clkname)			\
+	(_dev)->clks[0].clk = clk_get(NULL, _clkname);		\
+	(_dev)->nr_clks = 1;
+
+#define pdevice_num_clocks(_dev)	((_dev)->nr_clks)
+
+#define pdevice_clock(dev, idx)		((dev)->clks[(idx)].clk)
+
+/**
+ * clk_generic_notify -
+ *
+ * @code: the code event
+ * @dev: the platform_device under transaction
+ * @data: the clock event descriptor
+ *
+ * it's a generic notify function for devie with _only_
+ * one clock. It will :
+ * - accept every 'ENTER' state
+ * - suspend on 'PRE' state
+ * - resume on 'POST' state
+ * - do nothing on 'EXIT' state
+ */
+int clk_generic_notify(unsigned long code, struct platform_device *dev,
+	void *data);
+
+/*
+ * clk_generic_evaluate_rate
+ *
+ * @clk: the analised clock
+ * @prate: the parent rate
+ *
+ * Evaluate the clock rate (without hardware modification) based on a 'prate'
+ * parent clock rate. It's based on 'divisor' relationship
+ * between parent and child
+ */
+unsigned long clk_generic_evaluate_rate(struct clk *clk, unsigned long prate);
+#endif
 #endif
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index b67bb5d..db1989d 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -12,6 +12,7 @@
 #define _PLATFORM_DEVICE_H_
 
 #include <linux/device.h>
+#include <linux/clk.h>
 #include <linux/mod_devicetable.h>
 
 struct platform_device {
@@ -22,6 +23,11 @@ struct platform_device {
 	struct resource	* resource;
 
 	struct platform_device_id	*id_entry;
+#ifdef CONFIG_GENERIC_CLK_FM
+	unsigned long	clk_state;      /* used by the core */
+	unsigned long	nr_clks;
+	struct pdev_clk_info    *clks;
+#endif
 };
 
 #define platform_get_device_id(pdev)	((pdev)->id_entry)
@@ -61,6 +67,9 @@ struct platform_driver {
 	int (*resume_early)(struct platform_device *);
 	int (*resume)(struct platform_device *);
 	struct device_driver driver;
+#ifdef CONFIG_GENERIC_CLK_FM
+	int (*notify)(unsigned long code, struct platform_device *, void *);
+#endif
 	struct platform_device_id *id_table;
 };
 
diff --git a/init/Kconfig b/init/Kconfig
index 0682ecc..4254c5f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1042,6 +1042,29 @@ config SLOW_WORK
 
 	  See Documentation/slow-work.txt.
 
+config GENERIC_CLK_FM
+        default n
+	depends on EXPERIMENTAL
+        bool "Generic Clock Framework"
+        help
+          Add the clock framework in the Linux driver model
+          to track the clocks used by each devices and drivers
+
+config CLK_FORCE_GENERIC_EVALUATE
+        depends on GENERIC_CLK_FM
+        default n
+        bool "Force the clk_generic_evaluate_rate"
+        help
+          Say the if you want use the clk_generic_evaluate_rate on every clock
+          without evaluate_rate
+
+config CLK_DEBUG
+        depends on GENERIC_CLK_FM
+        default n
+        bool "Debug the Generic Clk Framework"
+        help
+          Prints some message to debug the clock framework
+
 endmenu		# General setup
 
 config HAVE_GENERIC_DMA_COHERENT
-- 
1.6.2.5


[-- Attachment #3: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [ANNOUNCE] ELC 2010 Call for Presentations
From: Tim Bird @ 2009-11-09 23:08 UTC (permalink / raw)
  To: linux-embedded

I apologize in advance for this non-technical post...

The CE Linux Forum would like to invite you to make a presentation
at our upcoming Embedded Linux Conference. The conference will be
held April 12-14, 2010 in San Francisco, California.

For general information about the conference, See
  http://embeddedlinuxconference.com/elc_2010/

For information about the call for presentations, see
  http://elinux.org/ELC_2010_Call_for_Presentations

CELF is the primary sponsor of this event, which is open to the
public.  Please note that the conference will be co-located with
the Linux Foundation Spring Collaboration Summit (April 14-16)
and it should be a very exciting event.

= Guidelines =
Presentations should be of a technical nature, covering topics
related to use of Linux in embedded systems. The CE Linux Forum
is focused on the use of Linux in consumer electronics products,
but presentations may cover use of Linux in other embedded
areas, as long as the topic is of general relevance to most
embedded developers.

Presentations that are commercial advertisements or sales
pitches are not appropriate for this conference.

Presentations on the following topics are encouraged:

 * Audio, Video, and Graphics systems for embedded products
 * Security
 * System size
 * Bootup time
 * Meeting real-time constraints
 * Power management
 * Streaming media
 * Flash memory devices and filesystems
 * Build systems
 * Embedded distributions
 * Development tools for embedded users
 * Technologies related to cell phones, digital settop boxes,
    televisions, cameras, handheld devices, or other CE products
 * Use of Linux in actual products, practical experience and
     war stories
 * Standards for CE products

Most presentation slots will be 50 minutes long, including time for
questions.

No paper submission is required in conjunction with the presentation.

Tutorials, demos, and Birds-of-a-Feather sessions may also be
proposed.

Full details, including the e-mail address to use to submit
a proposal, are at:
http://elinux.org/ELC_2010_Call_for_Presentations

The deadline for submissions is January 15, 2010.

Thanks,
 -- Tim

=============================
Tim Bird
Chair of the ELC Program Committee
Senior Staff Engineer, Sony Corporation of America
=============================

^ permalink raw reply

* Re: [[RFC] 4/5] An LED "dimmer" trigger, which uses the PWM API to  vary the brightness of an LED according to system load
From: Trilok Soni @ 2009-10-31  7:45 UTC (permalink / raw)
  To: Luotao Fu; +Cc: Bill Gatliff, Mike Frysinger, linux-embedded
In-Reply-To: <5d5443650910310043p1f82cf68i28bd5bfcdb9b8d7e@mail.gmail.com>

Hi Bill,

On Sat, Oct 31, 2009 at 1:13 PM, Trilok Soni <soni.trilok@gmail.com> wrote:
> Hi Luotao Fu,
>
> On Fri, Oct 30, 2009 at 7:33 PM, Luotao Fu <l.fu@pengutronix.de> wrote:
>> Hi,
>>
>> On Mon, Oct 19, 2009 at 08:42:36PM -0500, Bill Gatliff wrote:
>>> Mike Frysinger wrote:
>>> >On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
>>> >>--- a/drivers/leds/leds-pwm.c
>>> >>+++ b/drivers/leds/leds-pwm.c
>>> >>@@ -1,153 +1,167 @@
>>> >>-/*
>>> >>- * linux/drivers/leds-pwm.c
>>> >>- *
>>> >>- * simple PWM based LED control
>>> >>- *
>>> >>- * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
>>> >>- *
>>> >>- * based on leds-gpio.c by Raphael Assenat <raph@8d.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.
>>> >>- */
>>> >
>>> >this should not be removed.  if you wanted to add your copyright line,
>>> >then that's fine, but the rest needs to stay.
>>>
>>> For the record, the reason the file looks like it does is because I
>>> wrote an original one that replaced the previous leds-pwm.c--- but
>>> obviously git didn't see it that way when it produced the diff.
>>>
>>> I certainly wasn't trying to write-out anyone's copyright!  I don't
>>> have a problem with their names appearing in the file, regardless,
>>> so I'll put them back in.  If they have problems with their names
>>> appearing therein, they can let me know.  :)
>>
>> I certainly don't have any problems with that. ;-) As a matter of fact.
>> My driver was a really simple spinoff of the gpio led driver. Nothing
>> fancy in there. So I'm certainly happy that the driver will eventually
>> see a rewrite. I'm just a little surprsied as I caught up this mail
>> quite by occasion since I'm not subscribed to linux-embedded (time to do
>> it now, I think ;-)). 'd be nice if you could put the copyright hint
>> back in there. I'd test it by chance.
>>
>> The framework looks nice. The recent pwm stuff was more a loose policy
>> and spreaded in different places in kernel. Nice to have a real frame
>> work now. Nice work!
>
> Your observation is correct, and that's why I prefer to have this
> framework in mainline ASAP. It becomes more worst in the embedded
> system, when you would like to share the device drivers in open-source
> with different semiconductor vendor's SoCs. If you look at the Haptics
> patches on LKML, you will observe this problem, and why we need to
> have common PWM APIs in mainline.

Please always add LKML in CC for PWM framework patches to get more
reviewers :).

-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

^ permalink raw reply

* Re: [[RFC] 4/5] An LED "dimmer" trigger, which uses the PWM API to  vary the brightness of an LED according to system load
From: Trilok Soni @ 2009-10-31  7:43 UTC (permalink / raw)
  To: Luotao Fu; +Cc: Bill Gatliff, Mike Frysinger, linux-embedded
In-Reply-To: <20091030140337.GD4795@pengutronix.de>

Hi Luotao Fu,

On Fri, Oct 30, 2009 at 7:33 PM, Luotao Fu <l.fu@pengutronix.de> wrote:
> Hi,
>
> On Mon, Oct 19, 2009 at 08:42:36PM -0500, Bill Gatliff wrote:
>> Mike Frysinger wrote:
>> >On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
>> >>--- a/drivers/leds/leds-pwm.c
>> >>+++ b/drivers/leds/leds-pwm.c
>> >>@@ -1,153 +1,167 @@
>> >>-/*
>> >>- * linux/drivers/leds-pwm.c
>> >>- *
>> >>- * simple PWM based LED control
>> >>- *
>> >>- * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
>> >>- *
>> >>- * based on leds-gpio.c by Raphael Assenat <raph@8d.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.
>> >>- */
>> >
>> >this should not be removed.  if you wanted to add your copyright line,
>> >then that's fine, but the rest needs to stay.
>>
>> For the record, the reason the file looks like it does is because I
>> wrote an original one that replaced the previous leds-pwm.c--- but
>> obviously git didn't see it that way when it produced the diff.
>>
>> I certainly wasn't trying to write-out anyone's copyright!  I don't
>> have a problem with their names appearing in the file, regardless,
>> so I'll put them back in.  If they have problems with their names
>> appearing therein, they can let me know.  :)
>
> I certainly don't have any problems with that. ;-) As a matter of fact.
> My driver was a really simple spinoff of the gpio led driver. Nothing
> fancy in there. So I'm certainly happy that the driver will eventually
> see a rewrite. I'm just a little surprsied as I caught up this mail
> quite by occasion since I'm not subscribed to linux-embedded (time to do
> it now, I think ;-)). 'd be nice if you could put the copyright hint
> back in there. I'd test it by chance.
>
> The framework looks nice. The recent pwm stuff was more a loose policy
> and spreaded in different places in kernel. Nice to have a real frame
> work now. Nice work!

Your observation is correct, and that's why I prefer to have this
framework in mainline ASAP. It becomes more worst in the embedded
system, when you would like to share the device drivers in open-source
with different semiconductor vendor's SoCs. If you look at the Haptics
patches on LKML, you will observe this problem, and why we need to
have common PWM APIs in mainline.


-- 
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni

^ permalink raw reply

* [ANNOUNCE] New mailing list for u-boot-v2
From: Robert Schwebel @ 2009-10-30 20:28 UTC (permalink / raw)
  To: linux-embedded

Hi,

After Sascha Hauer's talk [1] about u-boot-v2 [2] at ELC-E [3] there was
an increasing demand for a separate mailing list for this new boot-
loader. So this is just a short note that, in the meantime, such a list
has been created on infradead [4].

Robert

PS: Many thanks to the organizing crew - it was really a great pleasure
    to meet so many of you guys in real life! :-)

[1] http://www.pengutronix.de/events/20091015-ELC-Europe/PRE-20091017-1-OSELAS.Training-U-Boot-V2.pdf
[2] http://www.pengutronix.de/software/u-boot/v2/index_en.html
[3] http://tree.celinuxforum.org/CelfPubWiki/ELCEurope2009Presentations
[4] http://lists.infradead.org/mailman/listinfo/u-boot-v2
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [[RFC] 4/5] An LED "dimmer" trigger, which uses the PWM API to vary the brightness of an LED according to system load
From: Luotao Fu @ 2009-10-30 14:03 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: Mike Frysinger, linux-embedded
In-Reply-To: <4ADD158C.2090708@billgatliff.com>

[-- Attachment #1: Type: text/plain, Size: 2281 bytes --]

Hi,

On Mon, Oct 19, 2009 at 08:42:36PM -0500, Bill Gatliff wrote:
> Mike Frysinger wrote:
> >On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
> >>--- a/drivers/leds/leds-pwm.c
> >>+++ b/drivers/leds/leds-pwm.c
> >>@@ -1,153 +1,167 @@
> >>-/*
> >>- * linux/drivers/leds-pwm.c
> >>- *
> >>- * simple PWM based LED control
> >>- *
> >>- * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
> >>- *
> >>- * based on leds-gpio.c by Raphael Assenat <raph@8d.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.
> >>- */
> >
> >this should not be removed.  if you wanted to add your copyright line,
> >then that's fine, but the rest needs to stay.
> 
> For the record, the reason the file looks like it does is because I
> wrote an original one that replaced the previous leds-pwm.c--- but
> obviously git didn't see it that way when it produced the diff.
> 
> I certainly wasn't trying to write-out anyone's copyright!  I don't
> have a problem with their names appearing in the file, regardless,
> so I'll put them back in.  If they have problems with their names
> appearing therein, they can let me know.  :)

I certainly don't have any problems with that. ;-) As a matter of fact.
My driver was a really simple spinoff of the gpio led driver. Nothing
fancy in there. So I'm certainly happy that the driver will eventually
see a rewrite. I'm just a little surprsied as I caught up this mail
quite by occasion since I'm not subscribed to linux-embedded (time to do
it now, I think ;-)). 'd be nice if you could put the copyright hint
back in there. I'd test it by chance.

The framework looks nice. The recent pwm stuff was more a loose policy
and spreaded in different places in kernel. Nice to have a real frame
work now. Nice work!

cheers
Luotao Fu
-- 
Pengutronix e.K.                           | Dipl.-Ing. Luotao Fu        |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: How to create a git repo on git.kernel.org?
From: George G. Davis @ 2009-10-20 14:28 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <4ADCD119.5070207@billgatliff.com>

On Mon, Oct 19, 2009 at 03:50:33PM -0500, Bill Gatliff wrote:
> Why is it that you don't find the relevant line in the FAQ until ten  
> seconds after clicking <send>?  :)

That's called an ohnosecond.  : )

--
Regards,
George

^ permalink raw reply

* Re: [[RFC] 4/5] An LED "dimmer" trigger, which uses the PWM API to  vary the brightness of an LED according to system load
From: Mike Frysinger @ 2009-10-20  3:58 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <4ADD158C.2090708@billgatliff.com>

On Mon, Oct 19, 2009 at 21:42, Bill Gatliff wrote:
> Mike Frysinger wrote:
>> On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
>>>  #include <linux/pwm.h>
>>> +#include <linux/pwm-led.h>
>>
>> if there's going to be a bunch of new pwm related headers, perhaps a
>> subdir makes more sense: linux/pwm/xxx
>
> In general I don't have a problem with this.  However, in this specific case
> would it make more sense to just fold pwm-led.h into pwm.h?  There really
> isn't much to pwm-led.h.

since pwm-led.h is meant for platform data and for boards to include
it, keeping it separate makes sense to me.  and once we start getting
more pwm based drivers that want to do headers of their own, having
linux/pwm/ for them would be nice.

as for the core pwm header, either linux/pwm.h or linux/pwm/pwm.h
would work i think.

>>> +static void
>>> +led_pwm_brightness_set(struct led_classdev *c,
>>> +                      enum led_brightness b)
>>> +{
>>> +       struct led_pwm *led;
>>> +       int percent;
>>> +
>>> +       percent = (b * 100) / (LED_FULL - LED_OFF);
>>> +       led = container_of(c, struct led_pwm, led);
>>
>> instead of using container_of() everywhere, why not add a helper macro
>> that turns a led_classev into a led_pwm
>
> That's just a personal style thing.  As soon as I write a helper macro, I
> tend to forget how it works and then start mis-using it everywhere.  But I
> don't have a problem with doing a helper.
>
> For better type-safety, would a helper inline-function be a better choice
> than a helper macro?  Or would it make any difference?

an inline function might be better.  it would also allow you to pass
in void* pointers though ...

>>> +       if (!try_module_get(d->driver->owner))
>>> +               return -ENODEV;
>>
>> is this really needed ?
>
> Not sure.

after looking at the other patches, i dont think it is.  the core pwm
framework does the module_get for you when you register the driver.
-mike

^ permalink raw reply

* Re: [[RFC] 3/5] Expunge old Atmel PWMC driver, replacing it with one that conforms to the PWM API
From: Bill Gatliff @ 2009-10-20  2:02 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-embedded
In-Reply-To: <8bd0f97a0910191534y12f9b6b9g1181507b20b50f80@mail.gmail.com>

Mike Frysinger wrote:
>> +       ap->clk = clk_get(&pdev->dev, "pwm_clk");
>> +       if (IS_ERR(ap->clk)) {
>> +               pr_info("%s: clk_get error %ld\n",
>> +                       ap->pwm.bus_id, PTR_ERR(ap->clk));
>> +               ret = -ENODEV;
>> +               goto err_clk_get;
>>     
>
> shouldnt it be:
>   ret = PTR_ERR(ap->clk);
>   

Probably, because it's preferable to return the actual error code when 
it's known, rather than translating all error codes to -ENODEV.  Good catch.


b.g.

-- 
Bill Gatliff
bgat@billgatliff.com

^ permalink raw reply

* Re: [[RFC] 1/5] API to consolidate PWM devices behind a common user and kernel interface
From: Bill Gatliff @ 2009-10-20  1:59 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-embedded
In-Reply-To: <8bd0f97a0910191529p690e10d9l9d6d22c2a02e67fb@mail.gmail.com>

Mike Frysinger wrote:
> On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
>   
>> +A generic PWM device framework must accomodate the substantial
>>     
>
> accommodate
>   

Heh, and to think I sometimes get paid to write!  :)

Similar and redundant feedback [snipped]


>> +synchronize, unsynchronize -- (optional) Callbacks to
>> +synchronize/unsynchronize channels.
>>     
>
> perhaps use desynchronize instead ?
>   

I like that idea.

>   
>> --- /dev/null
>> +++ b/drivers/pwm/pwm.c
>> @@ -0,0 +1,692 @@
>> +#define DEBUG 99
>>     
>
> whoops again
>   

Grr.  ADHD guys shouldn't code without their caffeine!  :)


>> +int pwm_register(struct pwm_device *pwm)
>> +{
>> +       p = kcalloc(pwm->nchan, sizeof(struct pwm_channel), GFP_KERNEL);
>>     
>
> sizeof(*p)
>   

Good catch.


>> +       pr_info("%s: %d channel%s\n", pwm->bus_id, pwm->nchan,
>> +               pwm->nchan > 1 ? "s" : "");
>>     
>
> dev_info(pwm->dev, ....)
>   

Yep.  I wonder what I was thinking at the time?  :)

>> +static struct pwm_device *
>> +__pwm_find_device(const char *bus_id)
>> +{
>> +       struct pwm_device *p;
>> +
>> +       list_for_each_entry(p, &pwm_device_list, list)
>> +       {
>>     
>
> cuddle that brace
>   

Yep.

>> +struct pwm_channel *
>> +pwm_request(const char *bus_id,
>> +           int chan,
>> +           const char *requester)
>> +{
>> +       struct pwm_device *p;
>> +
>> +       pr_debug("%s: %s:%d returns %p\n", __func__,
>> +                bus_id, chan, &p->channels[chan]);
>> +       pr_debug("%s: %s:%d returns NULL\n",
>> +                __func__, bus_id, chan);
>>     
>
> dev_dbg(p->dev, ....);
>   

Yep again, and again...

>   
>> +unsigned long pwm_ns_to_ticks(struct pwm_channel *p,
>> +                             unsigned long nsecs)
>> +{
>> +       unsigned long long ticks;
>> +
>> +       ticks = nsecs;
>> +       ticks *= p->tick_hz;
>> +       do_div(ticks, 1000000000);
>> +       return ticks;
>> +}
>> +EXPORT_SYMBOL(pwm_ns_to_ticks);
>>     
>
> perhaps better as a static inline in the header ?
>   

I guess that wouldn't negatively affect modules using the function, so I 
think I agree.


>> +unsigned long pwm_ticks_to_ns(struct pwm_channel *p,
>> +                             unsigned long ticks)
>> +{
>> +       unsigned long long ns;
>> +
>> +       if (!p->tick_hz)
>> +               return 0;
>> +
>> +       ns = ticks;
>> +       ns *= 1000000000UL;
>> +       do_div(ns, p->tick_hz);
>> +       return ns;
>> +}
>> +EXPORT_SYMBOL(pwm_ticks_to_ns);
>>     
>
> this one too
>   

Yep.

>> +static void
>> +pwm_config_percent_to_ticks(struct pwm_channel *p,
>> +                           struct pwm_channel_config *c)
>> +{
>> +       if (c->config_mask & PWM_CONFIG_DUTY_PERCENT) {
>> +               if (c->config_mask & PWM_CONFIG_PERIOD_TICKS)
>> +                       c->duty_ticks = c->period_ticks;
>> +               else
>> +                       c->duty_ticks = p->period_ticks;
>> +
>> +               c->duty_ticks *= c->duty_percent;
>> +               c->duty_ticks /= 100;
>> +               c->config_mask &= ~PWM_CONFIG_DUTY_PERCENT;
>> +               c->config_mask |= PWM_CONFIG_DUTY_TICKS;
>> +       }
>> +}
>>     
>
> if you returned when the mask doesnt match, then you wouldnt need to
> indent the rest of the func
>   

That's a habit I learned in my early days when I had an emulator that 
really didn't like multiple exit points from functions.  And it's been a 
hard habit to break!

>> +int pwm_config(struct pwm_channel *p,
>> +              struct pwm_channel_config *c)
>> +{
>> +       int ret = 0;
>> +
>> +       if (unlikely(!p->pwm->config)) {
>> +               pr_debug("%s: %s:%d has no config handler (-EINVAL)\n",
>> +                        __func__, p->pwm->bus_id, p->chan);
>>     
>
> dev_dbg(p->dev, ....);
>
>   
>> +       switch (c->config_mask & (PWM_CONFIG_PERIOD_TICKS
>> +                                 | PWM_CONFIG_DUTY_TICKS)) {
>> +       case PWM_CONFIG_PERIOD_TICKS:
>> +               if (p->duty_ticks > c->period_ticks) {
>> +                       ret = -EINVAL;
>> +                       goto err;
>> +               }
>> +               break;
>> +       case PWM_CONFIG_DUTY_TICKS:
>> +               if (p->period_ticks < c->duty_ticks) {
>> +                       ret = -EINVAL;
>> +                       goto err;
>> +               }
>> +               break;
>> +       case PWM_CONFIG_DUTY_TICKS | PWM_CONFIG_PERIOD_TICKS:
>> +               if (c->duty_ticks > c->period_ticks) {
>> +                       ret = -EINVAL;
>> +                       goto err;
>> +               }
>> +               break;
>>     
>
> unless i missed something, the first and third case is the same.  so
> probably better (compiled code wise) to write an if statement.
>   

Yea.  Can you tell that code has been "refactored"?  :)

>> +       pr_debug("%s: config_mask %d period_ticks %lu duty_ticks %lu"
>> +                " polarity %d duty_ns %lu period_ns %lu duty_percent %d\n",
>> +                __func__, c->config_mask, c->period_ticks, c->duty_ticks,
>> +                c->polarity, c->duty_ns, c->period_ns, c->duty_percent);
>>     
>
> dev_dbg(p->dev, ....);
>   

Yep.

>> +int pwm_set_period_ns(struct pwm_channel *p,
>> +                     unsigned long period_ns)
>> +unsigned long pwm_get_period_ns(struct pwm_channel *p)
>> +int pwm_set_duty_ns(struct pwm_channel *p,
>> +                   unsigned long duty_ns)
>> +unsigned long pwm_get_duty_ns(struct pwm_channel *p)
>> +int pwm_set_duty_percent(struct pwm_channel *p,
>> +                        int percent)
>> +int pwm_set_polarity(struct pwm_channel *p,
>> +                    int active_high)
>> +int pwm_start(struct pwm_channel *p)
>> +int pwm_stop(struct pwm_channel *p)
>>     
>
> these all look like they should static inlines
>   

I'll double-check each one, but I think you might be right.

>> +static void __pwm_callback(struct pwm_channel *p)
>> +{
>> +       queue_work(pwm_handler_workqueue, &p->handler_work);
>> +       pr_debug("%s:%d handler %p scheduled with data %p\n",
>> +                p->pwm->bus_id, p->chan, p->handler, p->handler_data);
>>     
>
> dev_dbg(p->dev, ....);
>   

Yep.


>> +static struct class pwm_class = {
>> +       .name = "pwm",
>> +       .owner = THIS_MODULE,
>> +
>> +       .class_attrs = pwm_class_attrs,
>> +};
>>     
>
> spurious new line ?
>   

Mentally, I separate the two activities and I guess my code reflects 
that.  :)


>> --- a/include/linux/pwm.h
>> +++ b/include/linux/pwm.h
>> @@ -1,31 +1,170 @@
>>  #ifndef __LINUX_PWM_H
>>  #define __LINUX_PWM_H
>>
>> -struct pwm_device;
>> -
>>  /*
>> - * pwm_request - request a PWM device
>> + * include/linux/pwm.h
>>     
>
> header #ifdef should really be after the comment blob
>   

Agreed.


>> +       PWM_CONFIG_DUTY_TICKS = BIT(0),
>> +       PWM_CONFIG_PERIOD_TICKS = BIT(1),
>> +       PWM_CONFIG_POLARITY = BIT(2),
>> +       PWM_CONFIG_START = BIT(3),
>> +       PWM_CONFIG_STOP = BIT(4),
>>
>> +       PWM_CONFIG_HANDLER = BIT(5),
>> +
>> +       PWM_CONFIG_DUTY_NS = BIT(6),
>>     
>
> spurious new lines ?
>   

More subtle mental "compartmentalizing" that needs to go away now that 
the code has stabilized.

>> +enum {
>> +       FLAG_REQUESTED = 0,
>> +       FLAG_STOP = 1,
>> +};
>>     
>
> this is weird ... you're doing things like:
>     if (pwm->channels[wchan].flags & FLAG_REQUESTED) {
>     if (test_and_set_bit(FLAG_REQUESTED, &p->flags))
>
> but FLAG_REQUESTED is 0 ...
>   

Yea, that first example is obviously very, very wrong.  The enumerations 
are supposed to be bit positions.


b.g.

-- 
Bill Gatliff
bgat@billgatliff.com

^ permalink raw reply

* Re: [[RFC] 2/5] Emulates PWM hardware using a high-resolution timer and a GPIO pin
From: Bill Gatliff @ 2009-10-20  1:47 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-embedded
In-Reply-To: <8bd0f97a0910191456n1b8f1aabr9b54b1126b2ec0c6@mail.gmail.com>

Mike Frysinger wrote:
> On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
>   
>> --- /dev/null
>> +++ b/drivers/pwm/gpio.c
>> @@ -0,0 +1,318 @@
>> +#define DEBUG 99
>>     
>
> whoops
>
>   

Indeed!


>> +       pr_debug("%s:%d start, %lu ticks\n",
>> +                dev_name(p->pwm->dev), p->chan, p->duty_ticks);
>>     
>
> you already have a struct device, so this is just odd.  use dev_dbg()
> instead and let it worry about dev_name() stuff.  plus you're already
> using dev_dbg() in the rest of the code, so i guess you just forgot
> about this.
>   

Yea, I think so.

>> +       case PWM_CONFIG_START:
>> +               if (!hrtimer_active(&gp->t)) {
>> +                       gpio_pwm_start(p);
>> +               }
>>     
>
> dont really need those braces
>   

Agreed.

>> +       struct gpio_pwm *gp = container_of(p->pwm, struct gpio_pwm, pwm);
>>     
>
> helper macro for this ?
>   

Ditto.

>> +static int
>> +gpio_pwm_config(struct pwm_channel *p,
>> +               struct pwm_channel_config *c)
>> +{
>> +       struct gpio_pwm *gp = container_of(p->pwm, struct gpio_pwm, pwm);
>> +       int was_on = 0;
>> +
>> +       if (p->pwm->config_nosleep) {
>> +               if (!p->pwm->config_nosleep(p, c))
>> +                       return 0;
>> +       }
>> +
>> +       might_sleep();
>>     
>
> shouldnt this be above the config_n
>   

Actually, not.  In this example I'm hoping that the requested action can 
be handled by the config_nosleep code, and if it can then I just 
return.  Otherwise, I go through the might_sleep and then into the code 
that might actually sleep.

But, I think the upper-level code might already do this same thing, so 
the above might be entirely redundant.  I'll have to check on that.


>> +static int __init
>> +gpio_pwm_probe(struct platform_device *pdev)
>>     
>
> __devinit
>   

Yep.

>> +static struct platform_driver gpio_pwm_driver = {
>> +       .driver = {
>> +               .name = "gpio_pwm",
>> +               .owner = THIS_MODULE,
>> +       },
>>     
>
> dont need the explicit .owner ?
>   

Again, not sure.


>   
>> +static void gpio_pwm_exit(void)
>>     
>
> __exit
>   

I think so, yes.


>> +MODULE_DESCRIPTION("PWM output using GPIO and an itimer");
>>     
>
> itimer -> hrtimer ?
>   

Yep.


b.g.

-- 
Bill Gatliff
bgat@billgatliff.com

^ permalink raw reply

* Re: [[RFC] 4/5] An LED "dimmer" trigger, which uses the PWM API to vary the brightness of an LED according to system load
From: Bill Gatliff @ 2009-10-20  1:42 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-embedded
In-Reply-To: <8bd0f97a0910191451w89d80ffx9afcdbe31dad0cc7@mail.gmail.com>

Mike Frysinger wrote:
> On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
>   
>> --- a/drivers/leds/leds-pwm.c
>> +++ b/drivers/leds/leds-pwm.c
>> @@ -1,153 +1,167 @@
>> -/*
>> - * linux/drivers/leds-pwm.c
>> - *
>> - * simple PWM based LED control
>> - *
>> - * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
>> - *
>> - * based on leds-gpio.c by Raphael Assenat <raph@8d.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.
>> - */
>>     
>
> this should not be removed.  if you wanted to add your copyright line,
> then that's fine, but the rest needs to stay.
>   

For the record, the reason the file looks like it does is because I 
wrote an original one that replaced the previous leds-pwm.c--- but 
obviously git didn't see it that way when it produced the diff.

I certainly wasn't trying to write-out anyone's copyright!  I don't have 
a problem with their names appearing in the file, regardless, so I'll 
put them back in.  If they have problems with their names appearing 
therein, they can let me know.  :)

>>  #include <linux/pwm.h>
>> +#include <linux/pwm-led.h>
>>     
>
> if there's going to be a bunch of new pwm related headers, perhaps a
> subdir makes more sense: linux/pwm/xxx
>   

In general I don't have a problem with this.  However, in this specific 
case would it make more sense to just fold pwm-led.h into pwm.h?  There 
really isn't much to pwm-led.h.

>> +static void
>> +led_pwm_brightness_set(struct led_classdev *c,
>> +                      enum led_brightness b)
>> +{
>> +       struct led_pwm *led;
>> +       int percent;
>> +
>> +       percent = (b * 100) / (LED_FULL - LED_OFF);
>> +       led = container_of(c, struct led_pwm, led);
>>     
>
> instead of using container_of() everywhere, why not add a helper macro
> that turns a led_classev into a led_pwm
>   

That's just a personal style thing.  As soon as I write a helper macro, 
I tend to forget how it works and then start mis-using it everywhere.  
But I don't have a problem with doing a helper.

For better type-safety, would a helper inline-function be a better 
choice than a helper macro?  Or would it make any difference?

>> +static int __init
>> +led_pwm_probe(struct platform_device *pdev)
>>     
>
> probe functions are typical __devinit
>   

Yep, my bad.

>> +       if (!try_module_get(d->driver->owner))
>> +               return -ENODEV;
>>     
>
> is this really needed ?
>   

Not sure.

>> -static int __devexit led_pwm_remove(struct platform_device *pdev)
>> +static int
>> +led_pwm_remove(struct platform_device *pdev)
>>     
>
> looks like you lost the __devexit markings
>   

Yep.

>>  static struct platform_driver led_pwm_driver = {
>> +       .driver = {
>> +               .name =         "leds-pwm",
>> +               .owner =        THIS_MODULE,
>>        },
>>     
>
> i dont think platform_drivers need to explicitly set their owner.  i
> thought that was all handled for you.
>   

There are examples for both cases.  I don't see anything assigning to 
.owner in drivers/base/platform.c, though.

>> --- /dev/null
>> +++ b/include/linux/pwm-led.h
>> @@ -0,0 +1,34 @@
>> +#ifndef __LINUX_PWM_LED_H
>> +#define __LINUX_PWM_LED_H
>> +
>> +/*
>> + * include/linux/pwm-led.h
>>     
>
> the ifdef protection typically goes after the header comment blob, not before
>   

Yep, my bad.


Thanks for the feedback!


b.g.

-- 
Bill Gatliff
bgat@billgatliff.com

^ permalink raw reply

* Re: [[RFC] 3/5] Expunge old Atmel PWMC driver, replacing it with one  that conforms to the PWM API
From: Mike Frysinger @ 2009-10-19 22:34 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <1255984366-26952-4-git-send-email-bgat@billgatliff.com>

On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
> --- /dev/null
> +++ b/drivers/pwm/atmel-pwm.c
> +       const struct atmel_pwm *ap
> +               = container_of(p->pwm, struct atmel_pwm, pwm);

make a helper ?

> +       pr_debug("%s:%d sync_mask %x\n",
> +                p->pwm->bus_id, p->chan, ap->sync_mask[p->chan]);

all of the pr_* funcs in this driver should be dev_* funcs

> +static int __init
> +atmel_pwmc_probe(struct platform_device *pdev)

__devinit

> +       ap->clk = clk_get(&pdev->dev, "pwm_clk");
> +       if (IS_ERR(ap->clk)) {
> +               pr_info("%s: clk_get error %ld\n",
> +                       ap->pwm.bus_id, PTR_ERR(ap->clk));
> +               ret = -ENODEV;
> +               goto err_clk_get;

shouldnt it be:
  ret = PTR_ERR(ap->clk);

> +static struct platform_driver atmel_pwm_driver = {
> +       .driver = {
> +               .name = "atmel_pwmc",
> +               .owner = THIS_MODULE,
> +       },

no need for .owner ?

> +static void atmel_pwm_exit(void)

__exit
-mike

^ permalink raw reply

* Re: [[RFC] 5/5] Incorporate PWM API code into KBuild
From: Mike Frysinger @ 2009-10-19 22:30 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <1255984366-26952-6-git-send-email-bgat@billgatliff.com>

On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -6,6 +6,8 @@
>  #
>
>  obj-y                          += gpio/
> +obj-$(CONFIG_GENERIC_PWM)      += pwm/
> +
>  obj-$(CONFIG_PCI)              += pci/

spurious new line

> --- /dev/null
> +++ b/drivers/pwm/Kconfig
> +         software controlled power-efficent backlights on LCD

efficient

> --- /dev/null
> +++ b/drivers/pwm/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for pwm devices
> +#
> +obj-y := pwm.o
> +
> +obj-$(CONFIG_ATMEL_PWM)                += atmel-pwm.o
> +obj-$(CONFIG_GPIO_PWM)         += gpio.o
> \ No newline at end of file

there should be a newline at the end of the file
-mike

^ permalink raw reply

* Re: [[RFC] 1/5] API to consolidate PWM devices behind a common user  and kernel interface
From: Mike Frysinger @ 2009-10-19 22:29 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <1255984366-26952-2-git-send-email-bgat@billgatliff.com>

On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
> +A generic PWM device framework must accomodate the substantial

accommodate

> +be accomodated by the Generic PWM Device API framework.

accommodated

> +bus_id -- the plaintext name of the device.  Users will bind to a

plain text

> +synchronize, unsynchronize -- (optional) Callbacks to
> +synchronize/unsynchronize channels.

perhaps use desynchronize instead ?

> --- /dev/null
> +++ b/drivers/pwm/pwm.c
> @@ -0,0 +1,692 @@
> +#define DEBUG 99

whoops again

> +int pwm_register(struct pwm_device *pwm)
> +{
> +       p = kcalloc(pwm->nchan, sizeof(struct pwm_channel), GFP_KERNEL);

sizeof(*p)

> +       pr_info("%s: %d channel%s\n", pwm->bus_id, pwm->nchan,
> +               pwm->nchan > 1 ? "s" : "");

dev_info(pwm->dev, ....)

> +static struct pwm_device *
> +__pwm_find_device(const char *bus_id)
> +{
> +       struct pwm_device *p;
> +
> +       list_for_each_entry(p, &pwm_device_list, list)
> +       {

cuddle that brace

> +struct pwm_channel *
> +pwm_request(const char *bus_id,
> +           int chan,
> +           const char *requester)
> +{
> +       struct pwm_device *p;
> +
> +       pr_debug("%s: %s:%d returns %p\n", __func__,
> +                bus_id, chan, &p->channels[chan]);
> +       pr_debug("%s: %s:%d returns NULL\n",
> +                __func__, bus_id, chan);

dev_dbg(p->dev, ....);

> +void pwm_free(struct pwm_channel *p)
> +{
> +       pr_debug("%s: %s:%d free\n",
> +                __func__, p->pwm->bus_id, p->chan);

dev_dbg(p->dev, ....);

> +unsigned long pwm_ns_to_ticks(struct pwm_channel *p,
> +                             unsigned long nsecs)
> +{
> +       unsigned long long ticks;
> +
> +       ticks = nsecs;
> +       ticks *= p->tick_hz;
> +       do_div(ticks, 1000000000);
> +       return ticks;
> +}
> +EXPORT_SYMBOL(pwm_ns_to_ticks);

perhaps better as a static inline in the header ?

> +unsigned long pwm_ticks_to_ns(struct pwm_channel *p,
> +                             unsigned long ticks)
> +{
> +       unsigned long long ns;
> +
> +       if (!p->tick_hz)
> +               return 0;
> +
> +       ns = ticks;
> +       ns *= 1000000000UL;
> +       do_div(ns, p->tick_hz);
> +       return ns;
> +}
> +EXPORT_SYMBOL(pwm_ticks_to_ns);

this one too

> +static void
> +pwm_config_percent_to_ticks(struct pwm_channel *p,
> +                           struct pwm_channel_config *c)
> +{
> +       if (c->config_mask & PWM_CONFIG_DUTY_PERCENT) {
> +               if (c->config_mask & PWM_CONFIG_PERIOD_TICKS)
> +                       c->duty_ticks = c->period_ticks;
> +               else
> +                       c->duty_ticks = p->period_ticks;
> +
> +               c->duty_ticks *= c->duty_percent;
> +               c->duty_ticks /= 100;
> +               c->config_mask &= ~PWM_CONFIG_DUTY_PERCENT;
> +               c->config_mask |= PWM_CONFIG_DUTY_TICKS;
> +       }
> +}

if you returned when the mask doesnt match, then you wouldnt need to
indent the rest of the func

> +int pwm_config(struct pwm_channel *p,
> +              struct pwm_channel_config *c)
> +{
> +       int ret = 0;
> +
> +       if (unlikely(!p->pwm->config)) {
> +               pr_debug("%s: %s:%d has no config handler (-EINVAL)\n",
> +                        __func__, p->pwm->bus_id, p->chan);

dev_dbg(p->dev, ....);

> +       switch (c->config_mask & (PWM_CONFIG_PERIOD_TICKS
> +                                 | PWM_CONFIG_DUTY_TICKS)) {
> +       case PWM_CONFIG_PERIOD_TICKS:
> +               if (p->duty_ticks > c->period_ticks) {
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
> +               break;
> +       case PWM_CONFIG_DUTY_TICKS:
> +               if (p->period_ticks < c->duty_ticks) {
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
> +               break;
> +       case PWM_CONFIG_DUTY_TICKS | PWM_CONFIG_PERIOD_TICKS:
> +               if (c->duty_ticks > c->period_ticks) {
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
> +               break;

unless i missed something, the first and third case is the same.  so
probably better (compiled code wise) to write an if statement.

> +       pr_debug("%s: config_mask %d period_ticks %lu duty_ticks %lu"
> +                " polarity %d duty_ns %lu period_ns %lu duty_percent %d\n",
> +                __func__, c->config_mask, c->period_ticks, c->duty_ticks,
> +                c->polarity, c->duty_ns, c->period_ns, c->duty_percent);

dev_dbg(p->dev, ....);

> +int pwm_set_period_ns(struct pwm_channel *p,
> +                     unsigned long period_ns)
> +unsigned long pwm_get_period_ns(struct pwm_channel *p)
> +int pwm_set_duty_ns(struct pwm_channel *p,
> +                   unsigned long duty_ns)
> +unsigned long pwm_get_duty_ns(struct pwm_channel *p)
> +int pwm_set_duty_percent(struct pwm_channel *p,
> +                        int percent)
> +int pwm_set_polarity(struct pwm_channel *p,
> +                    int active_high)
> +int pwm_start(struct pwm_channel *p)
> +int pwm_stop(struct pwm_channel *p)

these all look like they should static inlines

> +static void __pwm_callback(struct pwm_channel *p)
> +{
> +       queue_work(pwm_handler_workqueue, &p->handler_work);
> +       pr_debug("%s:%d handler %p scheduled with data %p\n",
> +                p->pwm->bus_id, p->chan, p->handler, p->handler_data);

dev_dbg(p->dev, ....);

> +static struct class pwm_class = {
> +       .name = "pwm",
> +       .owner = THIS_MODULE,
> +
> +       .class_attrs = pwm_class_attrs,
> +};

spurious new line ?

> --- a/include/linux/pwm.h
> +++ b/include/linux/pwm.h
> @@ -1,31 +1,170 @@
>  #ifndef __LINUX_PWM_H
>  #define __LINUX_PWM_H
>
> -struct pwm_device;
> -
>  /*
> - * pwm_request - request a PWM device
> + * include/linux/pwm.h

header #ifdef should really be after the comment blob

> +       PWM_CONFIG_DUTY_TICKS = BIT(0),
> +       PWM_CONFIG_PERIOD_TICKS = BIT(1),
> +       PWM_CONFIG_POLARITY = BIT(2),
> +       PWM_CONFIG_START = BIT(3),
> +       PWM_CONFIG_STOP = BIT(4),
>
> +       PWM_CONFIG_HANDLER = BIT(5),
> +
> +       PWM_CONFIG_DUTY_NS = BIT(6),

spurious new lines ?

> +enum {
> +       FLAG_REQUESTED = 0,
> +       FLAG_STOP = 1,
> +};

this is weird ... you're doing things like:
    if (pwm->channels[wchan].flags & FLAG_REQUESTED) {
    if (test_and_set_bit(FLAG_REQUESTED, &p->flags))

but FLAG_REQUESTED is 0 ...
-mike

^ permalink raw reply

* Re: [[RFC] 2/5] Emulates PWM hardware using a high-resolution timer  and a GPIO pin
From: Mike Frysinger @ 2009-10-19 21:56 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <1255984366-26952-3-git-send-email-bgat@billgatliff.com>

On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
> --- /dev/null
> +++ b/drivers/pwm/gpio.c
> @@ -0,0 +1,318 @@
> +#define DEBUG 99

whoops

> +       pr_debug("%s:%d start, %lu ticks\n",
> +                dev_name(p->pwm->dev), p->chan, p->duty_ticks);

you already have a struct device, so this is just odd.  use dev_dbg()
instead and let it worry about dev_name() stuff.  plus you're already
using dev_dbg() in the rest of the code, so i guess you just forgot
about this.

> +       case PWM_CONFIG_START:
> +               if (!hrtimer_active(&gp->t)) {
> +                       gpio_pwm_start(p);
> +               }

dont really need those braces

> +       struct gpio_pwm *gp = container_of(p->pwm, struct gpio_pwm, pwm);

helper macro for this ?

> +static int
> +gpio_pwm_config(struct pwm_channel *p,
> +               struct pwm_channel_config *c)
> +{
> +       struct gpio_pwm *gp = container_of(p->pwm, struct gpio_pwm, pwm);
> +       int was_on = 0;
> +
> +       if (p->pwm->config_nosleep) {
> +               if (!p->pwm->config_nosleep(p, c))
> +                       return 0;
> +       }
> +
> +       might_sleep();

shouldnt this be above the config_n

> +static int __init
> +gpio_pwm_probe(struct platform_device *pdev)

__devinit

> +static struct platform_driver gpio_pwm_driver = {
> +       .driver = {
> +               .name = "gpio_pwm",
> +               .owner = THIS_MODULE,
> +       },

dont need the explicit .owner ?

> +static void gpio_pwm_exit(void)

__exit

> +MODULE_DESCRIPTION("PWM output using GPIO and an itimer");

itimer -> hrtimer ?
-mike

^ permalink raw reply

* Re: [[RFC] 4/5] An LED "dimmer" trigger, which uses the PWM API to  vary the brightness of an LED according to system load
From: Mike Frysinger @ 2009-10-19 21:51 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <1255984366-26952-5-git-send-email-bgat@billgatliff.com>

On Mon, Oct 19, 2009 at 16:32, Bill Gatliff wrote:
> --- a/drivers/leds/leds-pwm.c
> +++ b/drivers/leds/leds-pwm.c
> @@ -1,153 +1,167 @@
> -/*
> - * linux/drivers/leds-pwm.c
> - *
> - * simple PWM based LED control
> - *
> - * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
> - *
> - * based on leds-gpio.c by Raphael Assenat <raph@8d.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.
> - */

this should not be removed.  if you wanted to add your copyright line,
then that's fine, but the rest needs to stay.

>  #include <linux/pwm.h>
> +#include <linux/pwm-led.h>

if there's going to be a bunch of new pwm related headers, perhaps a
subdir makes more sense: linux/pwm/xxx

> +static void
> +led_pwm_brightness_set(struct led_classdev *c,
> +                      enum led_brightness b)
> +{
> +       struct led_pwm *led;
> +       int percent;
> +
> +       percent = (b * 100) / (LED_FULL - LED_OFF);
> +       led = container_of(c, struct led_pwm, led);

instead of using container_of() everywhere, why not add a helper macro
that turns a led_classev into a led_pwm

> +static int __init
> +led_pwm_probe(struct platform_device *pdev)

probe functions are typical __devinit

> +       if (!try_module_get(d->driver->owner))
> +               return -ENODEV;

is this really needed ?

> -static int __devexit led_pwm_remove(struct platform_device *pdev)
> +static int
> +led_pwm_remove(struct platform_device *pdev)

looks like you lost the __devexit markings

>  static struct platform_driver led_pwm_driver = {
> +       .driver = {
> +               .name =         "leds-pwm",
> +               .owner =        THIS_MODULE,
>        },

i dont think platform_drivers need to explicitly set their owner.  i
thought that was all handled for you.

> --- /dev/null
> +++ b/include/linux/pwm-led.h
> @@ -0,0 +1,34 @@
> +#ifndef __LINUX_PWM_LED_H
> +#define __LINUX_PWM_LED_H
> +
> +/*
> + * include/linux/pwm-led.h

the ifdef protection typically goes after the header comment blob, not before
-mike

^ permalink raw reply

* Re: How to create a git repo on git.kernel.org?
From: Bill Gatliff @ 2009-10-19 20:50 UTC (permalink / raw)
  To: linux-embedded
In-Reply-To: <4ADCCECC.7080704@billgatliff.com>

Bill Gatliff wrote:
> Guys:
>
>
> How does one go about creating a git tree on git.kernel.org?  I'd like 
> to create one there as a public repository for the PWM API stuff, and 
> for a couple of boards that I'd like to get into mainline.
>
> Thanks!
>
> b.g.
>

Why is it that you don't find the relevant line in the FAQ until ten 
seconds after clicking <send>?  :)

Disregard. 


b.g.

-- 
Bill Gatliff
bgat@billgatliff.com

^ permalink raw reply

* Re: How to create a git repo on git.kernel.org?
From: Sam Ravnborg @ 2009-10-19 20:49 UTC (permalink / raw)
  To: Bill Gatliff; +Cc: linux-embedded
In-Reply-To: <4ADCCECC.7080704@billgatliff.com>

On Mon, Oct 19, 2009 at 03:40:44PM -0500, Bill Gatliff wrote:
> Guys:
>
>
> How does one go about creating a git tree on git.kernel.org?  I'd like  
> to create one there as a public repository for the PWM API stuff, and  
> for a couple of boards that I'd like to get into mainline.

Step one is to get an account on kernel.org.
Try to send an email to ftpadmin@kernel.org and explain why you
think you need an account / git tree there.

You will have to PGP sign your mail and having someone to recommend
you will help.

	Sam

^ permalink raw reply

* How to create a git repo on git.kernel.org?
From: Bill Gatliff @ 2009-10-19 20:40 UTC (permalink / raw)
  To: linux-embedded

Guys:


How does one go about creating a git tree on git.kernel.org?  I'd like 
to create one there as a public repository for the PWM API stuff, and 
for a couple of boards that I'd like to get into mainline.

Thanks!

b.g.

-- 
Bill Gatliff
bgat@billgatliff.com

^ permalink raw reply

* [[RFC] 5/5] Incorporate PWM API code into KBuild
From: Bill Gatliff @ 2009-10-19 20:32 UTC (permalink / raw)
  To: linux-embedded; +Cc: Bill Gatliff
In-Reply-To: <1255984366-26952-5-git-send-email-bgat@billgatliff.com>


Signed-off-by: Bill Gatliff <bgat@billgatliff.com>
---
 drivers/Kconfig      |    2 ++
 drivers/Makefile     |    2 ++
 drivers/pwm/Kconfig  |   30 ++++++++++++++++++++++++++++++
 drivers/pwm/Makefile |    7 +++++++
 4 files changed, 41 insertions(+), 0 deletions(-)
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 48bbdbe..ee45cf7 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -56,6 +56,8 @@ source "drivers/pps/Kconfig"
 
 source "drivers/gpio/Kconfig"
 
+source "drivers/pwm/Kconfig"
+
 source "drivers/w1/Kconfig"
 
 source "drivers/power/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 6ee53c7..e6143f3 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -6,6 +6,8 @@
 #
 
 obj-y				+= gpio/
+obj-$(CONFIG_GENERIC_PWM)	+= pwm/
+
 obj-$(CONFIG_PCI)		+= pci/
 obj-$(CONFIG_PARISC)		+= parisc/
 obj-$(CONFIG_RAPIDIO)		+= rapidio/
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 0000000..0ead279
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,30 @@
+#
+# PWM infrastructure and devices
+#
+
+menuconfig GENERIC_PWM
+	tristate "PWM Support"
+	help
+	  This enables PWM support through the generic PWM library.
+	  If unsure, say N.
+
+if GENERIC_PWM
+
+config ATMEL_PWM
+	tristate "Atmel AT32/AT91 PWM support"
+	depends on AVR32 || ARCH_AT91
+	help
+	  This option enables device driver support for the PWMC
+	  peripheral channels found on certain Atmel processors.
+	  Pulse Width Modulation is used many for purposes, including
+	  software controlled power-efficent backlights on LCD
+	  displays, motor control, and waveform generation.  If
+	  unsure, say N.
+
+config GPIO_PWM
+       tristate "PWM emulation using GPIO"
+       help
+         This option enables a single-channel PWM device using
+	 a kernel interval timer and a GPIO pin.  If unsure, say N.
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 0000000..e42246b
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for pwm devices
+#
+obj-y := pwm.o
+
+obj-$(CONFIG_ATMEL_PWM)		+= atmel-pwm.o
+obj-$(CONFIG_GPIO_PWM)		+= gpio.o
\ No newline at end of file
-- 
1.6.3.3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox