From: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
Cc: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>,
Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>,
Richard Zhao
<richard.zhao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Subject: [RFC PATCH 2/3] arm/imx: add a generic clock implementation for imx
Date: Tue, 22 Nov 2011 09:48:55 +0800 [thread overview]
Message-ID: <1321926536-671-3-git-send-email-shawn.guo@linaro.org> (raw)
In-Reply-To: <1321926536-671-1-git-send-email-shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
It adds a generic clock implementation which applies on several
modern imx generations.
Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
arch/arm/mach-imx/clock.c | 222 +++++++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-imx/clock.h | 64 +++++++++++++
2 files changed, 286 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-imx/clock.c
create mode 100644 arch/arm/mach-imx/clock.h
diff --git a/arch/arm/mach-imx/clock.c b/arch/arm/mach-imx/clock.c
new file mode 100644
index 0000000..6243622
--- /dev/null
+++ b/arch/arm/mach-imx/clock.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/processor.h>
+#include "clock.h"
+
+int clk_imx_enable(struct clk_hw *hw)
+{
+ struct clk_imx_gate *g = to_clk_imx(hw)->gate;
+ u32 val;
+
+ if (!g)
+ return 0;
+
+ val = readl_relaxed(g->reg);
+ if (g->gate_set_bit)
+ val &= ~g->mask;
+ else
+ val |= g->mask;
+ writel_relaxed(val, g->reg);
+
+ return 0;
+}
+
+void clk_imx_disable(struct clk_hw *hw)
+{
+ struct clk_imx_gate *g = to_clk_imx(hw)->gate;
+ u32 val;
+
+ if (!g)
+ return;
+
+ val = readl_relaxed(g->reg);
+ if (g->gate_set_bit)
+ val |= g->mask;
+ else
+ val &= ~g->mask;
+ writel_relaxed(val, g->reg);
+}
+
+struct clk *clk_imx_get_parent(struct clk_hw *hw)
+{
+ struct clk_imx_mux *m = to_clk_imx(hw)->mux;
+ u32 i;
+
+ if (!m)
+ return to_clk_imx(hw)->parent->clk;
+
+ i = readl_relaxed(m->reg) >> m->shift;
+ i &= (1 << m->width) - 1;
+
+ if (i >= m->num_parents)
+ return ERR_PTR(-EINVAL);
+
+ return m->parents[i]->clk;
+}
+
+static int clk_imx_busy_wait(struct clk_imx_busy *b)
+{
+ int timeout = 0x100000;
+
+ while ((readl_relaxed(b->reg) & b->mask) && --timeout)
+ cpu_relax();
+
+ if (unlikely(!timeout))
+ return -EBUSY;
+
+ return 0;
+}
+
+static int clk_imx_set_parent(struct clk_hw *hw, struct clk *parent)
+{
+ struct clk_imx_mux *m = to_clk_imx(hw)->mux;
+ int i;
+ u32 val;
+
+ if (!m)
+ return -EINVAL;
+
+ for (i = 0; i < m->num_parents; i++)
+ if (parent == m->parents[i]->clk)
+ break;
+
+ if (i == m->num_parents)
+ return -EINVAL;
+
+ val = readl_relaxed(m->reg);
+ val &= ~(((1 << m->width) - 1) << m->shift);
+ val |= i << m->shift;
+ writel_relaxed(val, m->reg);
+
+ return (m->busy) ? clk_imx_busy_wait(m->busy) : 0;
+}
+
+static unsigned long clk_imx_recalc_rate(struct clk_hw *hw)
+{
+ struct clk_imx_div *d = to_clk_imx(hw)->div;
+ u32 val, pred, podf;
+
+ if (!d)
+ return clk_get_rate(clk_get_parent(hw->clk));
+
+ val = readl_relaxed(d->reg);
+ pred = (val >> d->shift_pred & ((1 << d->width_pred) - 1)) + 1;
+ podf = (val >> d->shift_podf & ((1 << d->width_podf) - 1)) + 1;
+
+ return clk_get_rate(clk_get_parent(hw->clk)) / (pred * podf);
+}
+
+static void calc_pred_podf_dividers(u32 div, u32 *pred, u32 *podf)
+{
+ u32 min_pred, temp_pred, old_err, err;
+
+ if (div >= 512) {
+ *pred = 8;
+ *podf = 64;
+ } else if (div >= 8) {
+ min_pred = (div - 1) / 64 + 1;
+ old_err = 8;
+ for (temp_pred = 8; temp_pred >= min_pred; temp_pred--) {
+ err = div % temp_pred;
+ if (err == 0) {
+ *pred = temp_pred;
+ break;
+ }
+ err = temp_pred - err;
+ if (err < old_err) {
+ old_err = err;
+ *pred = temp_pred;
+ }
+ }
+ *podf = (div + *pred - 1) / *pred;
+ } else if (div < 8) {
+ *pred = div;
+ *podf = 1;
+ }
+}
+
+static long clk_imx_round_rate(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_imx_div *d = to_clk_imx(hw)->div;
+ u32 div, div_max, pred = 0, podf;
+ unsigned long parent_rate = clk_get_rate(clk_get_parent(hw->clk));
+
+ if (!d)
+ return -EINVAL;
+
+ div = parent_rate / rate;
+ if (div == 0 || parent_rate % rate)
+ div++;
+
+ if (d->width_pred) {
+ calc_pred_podf_dividers(div, &pred, &podf);
+ div = pred * podf;
+ } else {
+ div_max = 1 << d->width_podf;
+ if (div > div_max)
+ div = div_max;
+ }
+
+ return parent_rate / div;
+}
+
+static int clk_imx_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_imx_div *d = to_clk_imx(hw)->div;
+ u32 val, div, pred = 0, podf;
+
+ if (!d)
+ return -EINVAL;
+
+ *parent_rate = clk_get_rate(clk_get_parent(hw->clk));
+
+ div = *parent_rate / rate;
+ if (div == 0)
+ div++;
+
+ if ((*parent_rate / div != rate) || div >
+ (1 << d->width_pred) * (1 << d->width_podf))
+ return -EINVAL;
+
+ if (d->width_pred) {
+ calc_pred_podf_dividers(div, &pred, &podf);
+ } else {
+ pred = 1;
+ podf = div;
+ }
+
+ val = readl_relaxed(d->reg);
+ val &= ~(((1 << d->width_pred) - 1) << d->shift_pred);
+ val &= ~(((1 << d->width_podf) - 1) << d->shift_podf);
+ val |= (pred - 1) << d->shift_pred;
+ val |= (podf - 1) << d->shift_podf;
+ writel_relaxed(val, d->reg);
+
+ return (d->busy) ? clk_imx_busy_wait(d->busy) : 0;
+}
+
+const struct clk_hw_ops clk_imx_ops = {
+ .enable = clk_imx_enable,
+ .disable = clk_imx_disable,
+ .recalc_rate = clk_imx_recalc_rate,
+ .round_rate = clk_imx_round_rate,
+ .set_rate = clk_imx_set_rate,
+ .get_parent = clk_imx_get_parent,
+ .set_parent = clk_imx_set_parent,
+};
diff --git a/arch/arm/mach-imx/clock.h b/arch/arm/mach-imx/clock.h
new file mode 100644
index 0000000..49e42b9
--- /dev/null
+++ b/arch/arm/mach-imx/clock.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MACH_IMX_CLK_H
+#define __MACH_IMX_CLK_H
+
+#include <linux/types.h>
+#include <linux/clk.h>
+
+struct clk_imx_busy {
+ void __iomem *reg;
+ u32 mask;
+};
+
+struct clk_imx_gate {
+ void __iomem *reg;
+ u32 mask;
+ int gate_set_bit;
+ int powerup_set_bit;
+};
+
+struct clk_imx_div {
+ void __iomem *reg;
+ u32 shift_pred;
+ u32 width_pred;
+ u32 shift_podf;
+ u32 width_podf;
+ struct clk_imx_busy *busy;
+};
+
+struct clk_imx_mux {
+ void __iomem *reg;
+ u32 shift;
+ u32 width;
+ struct clk_imx_busy *busy;
+ struct clk_hw **parents;
+ int num_parents;
+};
+
+struct clk_hw_imx {
+ struct clk_hw hw;
+ struct clk_imx_gate *gate;
+ struct clk_imx_div *div;
+ struct clk_imx_mux *mux;
+ struct clk_hw *parent;
+};
+
+#define to_clk_imx(c) container_of(c, struct clk_hw_imx, hw)
+
+extern const struct clk_hw_ops clk_imx_ops;
+
+extern int clk_imx_enable(struct clk_hw *hw);
+extern void clk_imx_disable(struct clk_hw *hw);
+extern struct clk *clk_imx_get_parent(struct clk_hw *hw);
+
+#endif /* __MACH_IMX_CLK_H */
--
1.7.4.1
next prev parent reply other threads:[~2011-11-22 1:48 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-11-22 1:48 [RFC PATCH 0/3] Convert imx6 clock to device tree Shawn Guo
[not found] ` <1321926536-671-1-git-send-email-shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2011-11-22 1:48 ` [RFC PATCH 1/3] arm/imx6: describe clocks in device tree source Shawn Guo
[not found] ` <1321926536-671-2-git-send-email-shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2011-11-22 8:56 ` Sascha Hauer
[not found] ` <20111122085649.GC27267-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2011-11-24 4:01 ` Shawn Guo
[not found] ` <20111124040128.GC17979-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2011-11-24 8:17 ` Sascha Hauer
2011-11-22 1:48 ` Shawn Guo [this message]
2011-11-22 8:23 ` [RFC PATCH 0/3] Convert imx6 clock to device tree Sascha Hauer
[not found] ` <20111122082314.GB27267-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2011-11-24 2:59 ` Shawn Guo
[not found] ` <20111124025902.GB17979-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2011-11-24 8:49 ` Sascha Hauer
[not found] ` <20111124084923.GW27267-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2011-11-24 12:21 ` Shawn Guo
2011-11-22 1:48 ` [RFC PATCH 3/3] arm/imx6: convert " Shawn Guo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1321926536-671-3-git-send-email-shawn.guo@linaro.org \
--to=shawn.guo-qsej5fyqhm4dnm+yrofe0a@public.gmane.org \
--cc=devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org \
--cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
--cc=richard.zhao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
--cc=rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org \
--cc=s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).