Thread (29 messages) 29 messages, 9 authors, 2012-03-16

[PATCH v6 3/3] clk: basic clock hardware types

From: s.hauer@pengutronix.de (Sascha Hauer)
Date: 2012-03-14 18:24:16
Also in: lkml

On Fri, Mar 09, 2012 at 11:54:24PM -0800, Mike Turquette wrote:
quoted hunk ↗ jump to hunk
Many platforms support simple gateable clocks, fixed-rate clocks,
adjustable divider clocks and multi-parent multiplexer clocks.

This patch introduces basic clock types for the above-mentioned hardware
which share some common characteristics.

Based on original work by Jeremy Kerr and contribution by Jamie Iles.
Dividers and multiplexor clocks originally contributed by Richard Zhao &
Sascha Hauer.

Signed-off-by: Mike Turquette <redacted>
Signed-off-by: Mike Turquette <redacted>
Cc: Russell King <redacted>
Cc: Jeremy Kerr <redacted>
Cc: Thomas Gleixner <redacted>
Cc: Arnd Bergman <redacted>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Shawn Guo <redacted>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Jamie Iles <redacted>
Cc: Richard Zhao <redacted>
Cc: Saravana Kannan <redacted>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Rob Herring <redacted>
Cc: Mark Brown <redacted>
Cc: Linus Walleij <redacted>
Cc: Stephen Boyd <redacted>
Cc: Amit Kucheria <redacted>
Cc: Deepak Saxena <redacted>
Cc: Grant Likely <redacted>
Cc: Andrew Lunn <andrew@lunn.ch>
---
Changes since v5:
 * standardized the hw-specific locking in the basic clock types
 * export the individual ops for each basic clock type
 * improve registration for single-parent basic clocks (thanks Sascha)
 * fixed bugs in gate clock's static initializers (thanks Andrew)

 drivers/clk/Makefile         |    3 +-
 drivers/clk/clk-divider.c    |  204 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/clk-fixed-rate.c |   82 +++++++++++++++++
 drivers/clk/clk-gate.c       |  157 ++++++++++++++++++++++++++++++++
 drivers/clk/clk-mux.c        |  123 +++++++++++++++++++++++++
 include/linux/clk-private.h  |  124 +++++++++++++++++++++++++
 include/linux/clk-provider.h |  127 ++++++++++++++++++++++++++
 7 files changed, 819 insertions(+), 1 deletions(-)
 create mode 100644 drivers/clk/clk-divider.c
 create mode 100644 drivers/clk/clk-fixed-rate.c
 create mode 100644 drivers/clk/clk-gate.c
 create mode 100644 drivers/clk/clk-mux.c
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index ff362c4..1f736bc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,3 +1,4 @@
 
 obj-$(CONFIG_CLKDEV_LOOKUP)	+= clkdev.o
-obj-$(CONFIG_COMMON_CLK)	+= clk.o
+obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-fixed-rate.o clk-gate.o \
+				   clk-mux.o clk-divider.o
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
new file mode 100644
index 0000000..c0c4e0b
--- /dev/null
+++ b/drivers/clk/clk-divider.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * 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.
+ *
+ * Adjustable divider clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/*
+ * DOC: basic adjustable divider clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable.  clk->rate = parent->rate / divisor
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned int div;
+	unsigned long flags = 0;
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+
+	div = readl(divider->reg) >> divider->shift;
+	div &= (1 << divider->width) - 1;
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+
+	if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+		div++;
+
+	return parent_rate / div;
+}
+EXPORT_SYMBOL_GPL(clk_divider_recalc_rate);
+
+static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
+		unsigned long *best_parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	int i, bestdiv = 0;
+	unsigned long parent_rate, best = 0, now, maxdiv;
+
+	maxdiv = (1 << divider->width);
We need a check for rate == 0 here:

	if (rate == 0)
		rate = 1;

Otherwise we divide by zero below.
+
+	if (divider->flags & CLK_DIVIDER_ONE_BASED)
+		maxdiv--;
+
+	if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
+		parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+		bestdiv = parent_rate / rate;
It's a bit strange but due to integer maths you must use DIV_ROUND_UP
in round_rate and div = parent_rate / rate in set_rate.

Example:

parent_rate = 100
rate = 34

Now the best divider would be 3 (100/3 = 33), but the above
results in 2. With DIV_ROUND_UP round_rate correctly returns
33. Now when you use DIV_ROUND_UP in set_rate the resulting
divider would be 4 which is wrong whereas 100/33 returns the correct
result.

Sascha

-- 
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 |
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help