devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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

  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).