clk: add a fixed factor clock
Sascha Hauer [Thu, 3 May 2012 10:06:14 +0000 (15:06 +0530)]
Having fixed factors/dividers in hardware is a common pattern, so
add a basic clock type doing this. It basically describes a fixed
factor clock using a nominator and a denominator.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Viresh Kumar <viresh.kumar@st.com>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
[mturquette@linaro.org: constify parent_names in static init macro]
[mturquette@linaro.org: copy/paste bug from mux in static init macro]
[mturquette@linaro.org: fix error handling in clk_register_fixed_factor]
[mturquette@linaro.org: improve division accuracy; thanks to Saravana]
Signed-off-by: Mike Turquette <mturquette@linaro.org>

drivers/clk/Makefile
drivers/clk/clk-fixed-factor.c [new file with mode: 0644]
include/linux/clk-private.h
include/linux/clk-provider.h

index 1f736bc..24aa714 100644 (file)
@@ -1,4 +1,4 @@
 
 obj-$(CONFIG_CLKDEV_LOOKUP)    += clkdev.o
 obj-$(CONFIG_COMMON_CLK)       += clk.o clk-fixed-rate.o clk-gate.o \
-                                  clk-mux.o clk-divider.o
+                                  clk-mux.o clk-divider.o clk-fixed-factor.o
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
new file mode 100644 (file)
index 0000000..c8c003e
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ * 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.
+ *
+ * Standard functionality for the common clock API.
+ */
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic fixed multiplier and 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 fixed.  clk->rate = parent->rate / div * mult
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_fixed_factor(_hw) container_of(_hw, struct clk_fixed_factor, hw)
+
+static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
+
+       return parent_rate * fix->mult / fix->div;
+}
+
+static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
+{
+       struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
+
+       if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
+               unsigned long best_parent;
+
+               best_parent = (rate / fix->mult) * fix->div;
+               *prate = __clk_round_rate(__clk_get_parent(hw->clk),
+                               best_parent);
+       }
+
+       return (*prate / fix->div) * fix->mult;
+}
+
+static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       return 0;
+}
+
+struct clk_ops clk_fixed_factor_ops = {
+       .round_rate = clk_factor_round_rate,
+       .set_rate = clk_factor_set_rate,
+       .recalc_rate = clk_factor_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               unsigned int mult, unsigned int div)
+{
+       struct clk_fixed_factor *fix;
+       struct clk_init_data init;
+       struct clk *clk;
+
+       fix = kmalloc(sizeof(*fix), GFP_KERNEL);
+       if (!fix) {
+               pr_err("%s: could not allocate fixed factor clk\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       /* struct clk_fixed_factor assignments */
+       fix->mult = mult;
+       fix->div = div;
+       fix->hw.init = &init;
+
+       init.name = name;
+       init.ops = &clk_fixed_factor_ops;
+       init.flags = flags;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       clk = clk_register(dev, &fix->hw);
+
+       if (IS_ERR(clk))
+               kfree(fix);
+
+       return clk;
+}
index b258532..eb3f84b 100644 (file)
@@ -143,6 +143,26 @@ struct clk {
        DEFINE_CLK(_name, clk_mux_ops, _flags, _parent_names,   \
                        _parents);
 
+#define DEFINE_CLK_FIXED_FACTOR(_name, _parent_name,           \
+                               _parent_ptr, _flags,            \
+                               _mult, _div)                    \
+       static struct clk _name;                                \
+       static const char *_name##_parent_names[] = {           \
+               _parent_name,                                   \
+       };                                                      \
+       static struct clk *_name##_parents[] = {                \
+               _parent_ptr,                                    \
+       };                                                      \
+       static struct clk_fixed_factor _name##_hw = {           \
+               .hw = {                                         \
+                       .clk = &_name,                          \
+               },                                              \
+               .mult = _mult,                                  \
+               .div = _div,                                    \
+       };                                                      \
+       DEFINE_CLK(_name, clk_fixed_factor_ops, _flags,         \
+                       _name##_parent_names, _name##_parents);
+
 /**
  * __clk_init - initialize the data structures in a struct clk
  * @dev:       device initializing this clk, placeholder for now
index 5db3412..c1c23b9 100644 (file)
@@ -277,6 +277,29 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
                u8 clk_mux_flags, spinlock_t *lock);
 
 /**
+ * struct clk_fixed_factor - fixed multiplier and divider clock
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @mult:      multiplier
+ * @div:       divider
+ *
+ * Clock with a fixed multiplier and divider. The output frequency is the
+ * parent clock rate divided by div and multiplied by mult.
+ * Implements .recalc_rate, .set_rate and .round_rate
+ */
+
+struct clk_fixed_factor {
+       struct clk_hw   hw;
+       unsigned int    mult;
+       unsigned int    div;
+};
+
+extern struct clk_ops clk_fixed_factor_ops;
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               unsigned int mult, unsigned int div);
+
+/**
  * clk_register - allocate a new clock, register it and return an opaque cookie
  * @dev: device that is registering this clock
  * @hw: link to hardware-specific clock data