On Fri, Mar 09, 2012 at 11:54:24PM -0800, Mike Turquette wrote:
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 mturquette@linaro.org Signed-off-by: Mike Turquette mturquette@ti.com Cc: Russell King linux@arm.linux.org.uk Cc: Jeremy Kerr jeremy.kerr@canonical.com Cc: Thomas Gleixner tglx@linutronix.de Cc: Arnd Bergman arnd.bergmann@linaro.org Cc: Paul Walmsley paul@pwsan.com Cc: Shawn Guo shawn.guo@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de Cc: Jamie Iles jamie@jamieiles.com Cc: Richard Zhao richard.zhao@linaro.org Cc: Saravana Kannan skannan@codeaurora.org Cc: Magnus Damm magnus.damm@gmail.com Cc: Rob Herring rob.herring@calxeda.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Linus Walleij linus.walleij@stericsson.com Cc: Stephen Boyd sboyd@codeaurora.org Cc: Amit Kucheria amit.kucheria@linaro.org Cc: Deepak Saxena dsaxena@linaro.org Cc: Grant Likely grant.likely@secretlab.ca 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