]> Pileus Git - ~andy/linux/commitdiff
clk: basic clock hardware types
authorMike Turquette <mturquette@linaro.org>
Fri, 16 Mar 2012 06:11:20 +0000 (23:11 -0700)
committerArnd Bergmann <arnd@arndb.de>
Fri, 16 Mar 2012 20:35:02 +0000 (20:35 +0000)
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>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Rob Herring <rob.herring@calxeda.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: 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>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
drivers/clk/Makefile
drivers/clk/clk-divider.c [new file with mode: 0644]
drivers/clk/clk-fixed-rate.c [new file with mode: 0644]
drivers/clk/clk-gate.c [new file with mode: 0644]
drivers/clk/clk-mux.c [new file with mode: 0644]
include/linux/clk-private.h
include/linux/clk-provider.h

index ff362c4fb4c34e44db2bd647e7f02b4236ebd04d..1f736bc11c4bb26f887c4480d9484c64274a0d90 100644 (file)
@@ -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 (file)
index 0000000..d5ac6a7
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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)
+
+#define div_mask(d)    ((1 << (d->width)) - 1)
+
+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;
+
+       div = readl(divider->reg) >> divider->shift;
+       div &= div_mask(divider);
+
+       if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+               div++;
+
+       return parent_rate / div;
+}
+EXPORT_SYMBOL_GPL(clk_divider_recalc_rate);
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+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;
+
+       if (!rate)
+               rate = 1;
+
+       maxdiv = (1 << divider->width);
+
+       if (divider->flags & CLK_DIVIDER_ONE_BASED)
+               maxdiv--;
+
+       if (!best_parent_rate) {
+               parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+               bestdiv = DIV_ROUND_UP(parent_rate, rate);
+               bestdiv = bestdiv == 0 ? 1 : bestdiv;
+               bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
+               return bestdiv;
+       }
+
+       /*
+        * The maximum divider we can use without overflowing
+        * unsigned long in rate * i below
+        */
+       maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+       for (i = 1; i <= maxdiv; i++) {
+               parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
+                               MULT_ROUND_UP(rate, i));
+               now = parent_rate / i;
+               if (now <= rate && now > best) {
+                       bestdiv = i;
+                       best = now;
+                       *best_parent_rate = parent_rate;
+               }
+       }
+
+       if (!bestdiv) {
+               bestdiv = (1 << divider->width);
+               if (divider->flags & CLK_DIVIDER_ONE_BASED)
+                       bestdiv--;
+               *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
+       }
+
+       return bestdiv;
+}
+
+static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
+{
+       int div;
+       div = clk_divider_bestdiv(hw, rate, prate);
+
+       if (prate)
+               return *prate / div;
+       else {
+               unsigned long r;
+               r = __clk_get_rate(__clk_get_parent(hw->clk));
+               return r / div;
+       }
+}
+EXPORT_SYMBOL_GPL(clk_divider_round_rate);
+
+static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate)
+{
+       struct clk_divider *divider = to_clk_divider(hw);
+       unsigned int div;
+       unsigned long flags = 0;
+       u32 val;
+
+       div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate;
+
+       if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+               div--;
+
+       if (div > div_mask(divider))
+               div = div_mask(divider);
+
+       if (divider->lock)
+               spin_lock_irqsave(divider->lock, flags);
+
+       val = readl(divider->reg);
+       val &= ~(div_mask(divider) << divider->shift);
+       val |= div << divider->shift;
+       writel(val, divider->reg);
+
+       if (divider->lock)
+               spin_unlock_irqrestore(divider->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(clk_divider_set_rate);
+
+struct clk_ops clk_divider_ops = {
+       .recalc_rate = clk_divider_recalc_rate,
+       .round_rate = clk_divider_round_rate,
+       .set_rate = clk_divider_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_ops);
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               void __iomem *reg, u8 shift, u8 width,
+               u8 clk_divider_flags, spinlock_t *lock)
+{
+       struct clk_divider *div;
+       struct clk *clk;
+
+       div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+
+       if (!div) {
+               pr_err("%s: could not allocate divider clk\n", __func__);
+               return NULL;
+       }
+
+       /* struct clk_divider assignments */
+       div->reg = reg;
+       div->shift = shift;
+       div->width = width;
+       div->flags = clk_divider_flags;
+       div->lock = lock;
+
+       if (parent_name) {
+               div->parent[0] = kstrdup(parent_name, GFP_KERNEL);
+               if (!div->parent[0])
+                       goto out;
+       }
+
+       clk = clk_register(dev, name,
+                       &clk_divider_ops, &div->hw,
+                       div->parent,
+                       (parent_name ? 1 : 0),
+                       flags);
+       if (clk)
+               return clk;
+
+out:
+       kfree(div->parent[0]);
+       kfree(div);
+
+       return NULL;
+}
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
new file mode 100644 (file)
index 0000000..90c79fb
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * 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.
+ *
+ * Fixed rate clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic fixed-rate clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parents are prepared
+ * enable - clk_enable only ensures parents are enabled
+ * rate - rate is always a fixed value.  No clk_set_rate support
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)
+
+static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return to_clk_fixed_rate(hw)->fixed_rate;
+}
+EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate);
+
+struct clk_ops clk_fixed_rate_ops = {
+       .recalc_rate = clk_fixed_rate_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
+
+struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               unsigned long fixed_rate)
+{
+       struct clk_fixed_rate *fixed;
+       char **parent_names = NULL;
+       u8 len;
+
+       fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+
+       if (!fixed) {
+               pr_err("%s: could not allocate fixed clk\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       /* struct clk_fixed_rate assignments */
+       fixed->fixed_rate = fixed_rate;
+
+       if (parent_name) {
+               parent_names = kmalloc(sizeof(char *), GFP_KERNEL);
+
+               if (! parent_names)
+                       goto out;
+
+               len = sizeof(char) * strlen(parent_name);
+
+               parent_names[0] = kmalloc(len, GFP_KERNEL);
+
+               if (!parent_names[0])
+                       goto out;
+
+               strncpy(parent_names[0], parent_name, len);
+       }
+
+out:
+       return clk_register(dev, name,
+                       &clk_fixed_rate_ops, &fixed->hw,
+                       parent_names,
+                       (parent_name ? 1 : 0),
+                       flags);
+}
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
new file mode 100644 (file)
index 0000000..b5902e2
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * 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.
+ *
+ * Gated 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 gatable clock which can gate and ungate it's ouput
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - inherits rate from parent.  No clk_set_rate support
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+
+static void clk_gate_set_bit(struct clk_gate *gate)
+{
+       u32 reg;
+       unsigned long flags = 0;
+
+       if (gate->lock)
+               spin_lock_irqsave(gate->lock, flags);
+
+       reg = readl(gate->reg);
+       reg |= BIT(gate->bit_idx);
+       writel(reg, gate->reg);
+
+       if (gate->lock)
+               spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static void clk_gate_clear_bit(struct clk_gate *gate)
+{
+       u32 reg;
+       unsigned long flags = 0;
+
+       if (gate->lock)
+               spin_lock_irqsave(gate->lock, flags);
+
+       reg = readl(gate->reg);
+       reg &= ~BIT(gate->bit_idx);
+       writel(reg, gate->reg);
+
+       if (gate->lock)
+               spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int clk_gate_enable(struct clk_hw *hw)
+{
+       struct clk_gate *gate = to_clk_gate(hw);
+
+       if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+               clk_gate_clear_bit(gate);
+       else
+               clk_gate_set_bit(gate);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_enable);
+
+static void clk_gate_disable(struct clk_hw *hw)
+{
+       struct clk_gate *gate = to_clk_gate(hw);
+
+       if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+               clk_gate_set_bit(gate);
+       else
+               clk_gate_clear_bit(gate);
+}
+EXPORT_SYMBOL_GPL(clk_gate_disable);
+
+static int clk_gate_is_enabled(struct clk_hw *hw)
+{
+       u32 reg;
+       struct clk_gate *gate = to_clk_gate(hw);
+
+       reg = readl(gate->reg);
+
+       /* if a set bit disables this clk, flip it before masking */
+       if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+               reg ^= BIT(gate->bit_idx);
+
+       reg &= BIT(gate->bit_idx);
+
+       return reg ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_is_enabled);
+
+struct clk_ops clk_gate_ops = {
+       .enable = clk_gate_enable,
+       .disable = clk_gate_disable,
+       .is_enabled = clk_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gate_ops);
+
+struct clk *clk_register_gate(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               void __iomem *reg, u8 bit_idx,
+               u8 clk_gate_flags, spinlock_t *lock)
+{
+       struct clk_gate *gate;
+       struct clk *clk;
+
+       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+
+       if (!gate) {
+               pr_err("%s: could not allocate gated clk\n", __func__);
+               return NULL;
+       }
+
+       /* struct clk_gate assignments */
+       gate->reg = reg;
+       gate->bit_idx = bit_idx;
+       gate->flags = clk_gate_flags;
+       gate->lock = lock;
+
+       if (parent_name) {
+               gate->parent[0] = kstrdup(parent_name, GFP_KERNEL);
+               if (!gate->parent[0])
+                       goto out;
+       }
+
+       clk = clk_register(dev, name,
+                       &clk_gate_ops, &gate->hw,
+                       gate->parent,
+                       (parent_name ? 1 : 0),
+                       flags);
+       if (clk)
+               return clk;
+out:
+       kfree(gate->parent[0]);
+       kfree(gate);
+
+       return NULL;
+}
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
new file mode 100644 (file)
index 0000000..c71ad1f
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ *
+ * Simple multiplexer clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic adjustable multiplexer 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 only affected by parent switching.  No clk_set_rate support
+ * parent - parent is adjustable through clk_set_parent
+ */
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static u8 clk_mux_get_parent(struct clk_hw *hw)
+{
+       struct clk_mux *mux = to_clk_mux(hw);
+       u32 val;
+
+       /*
+        * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+        * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+        * to 0x7 (index starts at one)
+        * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+        * val = 0x4 really means "bit 2, index starts at bit 0"
+        */
+       val = readl(mux->reg) >> mux->shift;
+       val &= (1 << mux->width) - 1;
+
+       if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+               val = ffs(val) - 1;
+
+       if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+               val--;
+
+       if (val >= __clk_get_num_parents(hw->clk))
+               return -EINVAL;
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk_mux *mux = to_clk_mux(hw);
+       u32 val;
+       unsigned long flags = 0;
+
+       if (mux->flags & CLK_MUX_INDEX_BIT)
+               index = (1 << ffs(index));
+
+       if (mux->flags & CLK_MUX_INDEX_ONE)
+               index++;
+
+       if (mux->lock)
+               spin_lock_irqsave(mux->lock, flags);
+
+       val = readl(mux->reg);
+       val &= ~(((1 << mux->width) - 1) << mux->shift);
+       val |= index << mux->shift;
+       writel(val, mux->reg);
+
+       if (mux->lock)
+               spin_unlock_irqrestore(mux->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(clk_mux_set_parent);
+
+struct clk_ops clk_mux_ops = {
+       .get_parent = clk_mux_get_parent,
+       .set_parent = clk_mux_set_parent,
+};
+EXPORT_SYMBOL_GPL(clk_mux_ops);
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+               char **parent_names, u8 num_parents, unsigned long flags,
+               void __iomem *reg, u8 shift, u8 width,
+               u8 clk_mux_flags, spinlock_t *lock)
+{
+       struct clk_mux *mux;
+
+       mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL);
+
+       if (!mux) {
+               pr_err("%s: could not allocate mux clk\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       /* struct clk_mux assignments */
+       mux->reg = reg;
+       mux->shift = shift;
+       mux->width = width;
+       mux->flags = clk_mux_flags;
+       mux->lock = lock;
+
+       return clk_register(dev, name, &clk_mux_ops, &mux->hw,
+                       parent_names, num_parents, flags);
+}
index e2cce6fa604686ea3bca0496998322bacaed854d..5e4312b6f5ccb1072273ce94e235874c74dd852d 100644 (file)
@@ -46,6 +46,130 @@ struct clk {
 #endif
 };
 
+/*
+ * DOC: Basic clock implementations common to many platforms
+ *
+ * Each basic clock hardware type is comprised of a structure describing the
+ * clock hardware, implementations of the relevant callbacks in struct clk_ops,
+ * unique flags for that hardware type, a registration function and an
+ * alternative macro for static initialization
+ */
+
+extern struct clk_ops clk_fixed_rate_ops;
+
+#define DEFINE_CLK_FIXED_RATE(_name, _flags, _rate,            \
+                               _fixed_rate_flags)              \
+       static struct clk _name;                                \
+       static char *_name##_parent_names[] = {};               \
+       static struct clk_fixed_rate _name##_hw = {             \
+               .hw = {                                         \
+                       .clk = &_name,                          \
+               },                                              \
+               .fixed_rate = _rate,                            \
+               .flags = _fixed_rate_flags,                     \
+       };                                                      \
+       static struct clk _name = {                             \
+               .name = #_name,                                 \
+               .ops = &clk_fixed_rate_ops,                     \
+               .hw = &_name##_hw.hw,                           \
+               .parent_names = _name##_parent_names,           \
+               .num_parents =                                  \
+                       ARRAY_SIZE(_name##_parent_names),       \
+               .flags = _flags,                                \
+       };
+
+extern struct clk_ops clk_gate_ops;
+
+#define DEFINE_CLK_GATE(_name, _parent_name, _parent_ptr,      \
+                               _flags, _reg, _bit_idx,         \
+                               _gate_flags, _lock)             \
+       static struct clk _name;                                \
+       static char *_name##_parent_names[] = {                 \
+               _parent_name,                                   \
+       };                                                      \
+       static struct clk *_name##_parents[] = {                \
+               _parent_ptr,                                    \
+       };                                                      \
+       static struct clk_gate _name##_hw = {                   \
+               .hw = {                                         \
+                       .clk = &_name,                          \
+               },                                              \
+               .reg = _reg,                                    \
+               .bit_idx = _bit_idx,                            \
+               .flags = _gate_flags,                           \
+               .lock = _lock,                                  \
+       };                                                      \
+       static struct clk _name = {                             \
+               .name = #_name,                                 \
+               .ops = &clk_gate_ops,                           \
+               .hw = &_name##_hw.hw,                           \
+               .parent_names = _name##_parent_names,           \
+               .num_parents =                                  \
+                       ARRAY_SIZE(_name##_parent_names),       \
+               .parents = _name##_parents,                     \
+               .flags = _flags,                                \
+       };
+
+extern struct clk_ops clk_divider_ops;
+
+#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr,   \
+                               _flags, _reg, _shift, _width,   \
+                               _divider_flags, _lock)          \
+       static struct clk _name;                                \
+       static char *_name##_parent_names[] = {                 \
+               _parent_name,                                   \
+       };                                                      \
+       static struct clk *_name##_parents[] = {                \
+               _parent_ptr,                                    \
+       };                                                      \
+       static struct clk_divider _name##_hw = {                \
+               .hw = {                                         \
+                       .clk = &_name,                          \
+               },                                              \
+               .reg = _reg,                                    \
+               .shift = _shift,                                \
+               .width = _width,                                \
+               .flags = _divider_flags,                        \
+               .lock = _lock,                                  \
+       };                                                      \
+       static struct clk _name = {                             \
+               .name = #_name,                                 \
+               .ops = &clk_divider_ops,                        \
+               .hw = &_name##_hw.hw,                           \
+               .parent_names = _name##_parent_names,           \
+               .num_parents =                                  \
+                       ARRAY_SIZE(_name##_parent_names),       \
+               .parents = _name##_parents,                     \
+               .flags = _flags,                                \
+       };
+
+extern struct clk_ops clk_mux_ops;
+
+#define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \
+                               _reg, _shift, _width,           \
+                               _mux_flags, _lock)              \
+       static struct clk _name;                                \
+       static struct clk_mux _name##_hw = {                    \
+               .hw = {                                         \
+                       .clk = &_name,                          \
+               },                                              \
+               .reg = _reg,                                    \
+               .shift = _shift,                                \
+               .width = _width,                                \
+               .flags = _mux_flags,                            \
+               .lock = _lock,                                  \
+       };                                                      \
+       static struct clk _name = {                             \
+               .name = #_name,                                 \
+               .ops = &clk_mux_ops,                            \
+               .hw = &_name##_hw.hw,                           \
+               .parent_names = _parent_names,                  \
+               .num_parents =                                  \
+                       ARRAY_SIZE(_parent_names),              \
+               .parents = _parents,                            \
+               .flags = _flags,                                \
+       };
+
 /**
  * __clk_init - initialize the data structures in a struct clk
  * @dev:       device initializing this clk, placeholder for now
index b18b0e7888a81a67ea9740b476d65a8caf3bd10d..5508897ad376054102e263958693573260ebe23b 100644 (file)
@@ -129,6 +129,133 @@ struct clk_ops {
        void            (*init)(struct clk_hw *hw);
 };
 
+/*
+ * DOC: Basic clock implementations common to many platforms
+ *
+ * Each basic clock hardware type is comprised of a structure describing the
+ * clock hardware, implementations of the relevant callbacks in struct clk_ops,
+ * unique flags for that hardware type, a registration function and an
+ * alternative macro for static initialization
+ */
+
+/**
+ * struct clk_fixed_rate - fixed-rate clock
+ * @hw:                handle between common and hardware-specific interfaces
+ * @fixed_rate:        constant frequency of clock
+ */
+struct clk_fixed_rate {
+       struct          clk_hw hw;
+       unsigned long   fixed_rate;
+       u8              flags;
+};
+
+struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               unsigned long fixed_rate);
+
+/**
+ * struct clk_gate - gating clock
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @reg:       register controlling gate
+ * @bit_idx:   single bit controlling gate
+ * @flags:     hardware-specific flags
+ * @lock:      register lock
+ *
+ * Clock which can gate its output.  Implements .enable & .disable
+ *
+ * Flags:
+ * CLK_GATE_SET_DISABLE - by default this clock sets the bit at bit_idx to
+ *     enable the clock.  Setting this flag does the opposite: setting the bit
+ *     disable the clock and clearing it enables the clock
+ */
+struct clk_gate {
+       struct clk_hw hw;
+       void __iomem    *reg;
+       u8              bit_idx;
+       u8              flags;
+       spinlock_t      *lock;
+       char            *parent[1];
+};
+
+#define CLK_GATE_SET_TO_DISABLE                BIT(0)
+
+struct clk *clk_register_gate(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               void __iomem *reg, u8 bit_idx,
+               u8 clk_gate_flags, spinlock_t *lock);
+
+/**
+ * struct clk_divider - adjustable divider clock
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @reg:       register containing the divider
+ * @shift:     shift to the divider bit field
+ * @width:     width of the divider bit field
+ * @lock:      register lock
+ *
+ * Clock with an adjustable divider affecting its output frequency.  Implements
+ * .recalc_rate, .set_rate and .round_rate
+ *
+ * Flags:
+ * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the
+ *     register plus one.  If CLK_DIVIDER_ONE_BASED is set then the divider is
+ *     the raw value read from the register, with the value of zero considered
+ *     invalid
+ * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from
+ *     the hardware register
+ */
+struct clk_divider {
+       struct clk_hw   hw;
+       void __iomem    *reg;
+       u8              shift;
+       u8              width;
+       u8              flags;
+       spinlock_t      *lock;
+       char            *parent[1];
+};
+
+#define CLK_DIVIDER_ONE_BASED          BIT(0)
+#define CLK_DIVIDER_POWER_OF_TWO       BIT(1)
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               void __iomem *reg, u8 shift, u8 width,
+               u8 clk_divider_flags, spinlock_t *lock);
+
+/**
+ * struct clk_mux - multiplexer clock
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @reg:       register controlling multiplexer
+ * @shift:     shift to multiplexer bit field
+ * @width:     width of mutliplexer bit field
+ * @num_clks:  number of parent clocks
+ * @lock:      register lock
+ *
+ * Clock with multiple selectable parents.  Implements .get_parent, .set_parent
+ * and .recalc_rate
+ *
+ * Flags:
+ * CLK_MUX_INDEX_ONE - register index starts at 1, not 0
+ * CLK_MUX_INDEX_BITWISE - register index is a single bit (power of two)
+ */
+struct clk_mux {
+       struct clk_hw   hw;
+       void __iomem    *reg;
+       u8              shift;
+       u8              width;
+       u8              flags;
+       spinlock_t      *lock;
+};
+
+#define CLK_MUX_INDEX_ONE              BIT(0)
+#define CLK_MUX_INDEX_BIT              BIT(1)
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+               char **parent_names, u8 num_parents, unsigned long flags,
+               void __iomem *reg, u8 shift, u8 width,
+               u8 clk_mux_flags, spinlock_t *lock);
 
 /**
  * clk_register - allocate a new clock, register it and return an opaque cookie