From patchwork Tue Nov 22 01:48:55 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shawn Guo X-Patchwork-Id: 126985 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AAF38B6F8A for ; Tue, 22 Nov 2011 12:48:45 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1RSfQM-0003Lz-IB; Tue, 22 Nov 2011 01:45:26 +0000 Received: from mail-iy0-f177.google.com ([209.85.210.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1RSfOt-0002oU-Ni for linux-arm-kernel@lists.infradead.org; Tue, 22 Nov 2011 01:44:04 +0000 Received: by iapp10 with SMTP id p10so10216722iap.36 for ; Mon, 21 Nov 2011 17:43:54 -0800 (PST) Received: by 10.42.151.196 with SMTP id f4mr16401723icw.17.1321926232169; Mon, 21 Nov 2011 17:43:52 -0800 (PST) Received: from localhost.localdomain ([49.73.90.195]) by mx.google.com with ESMTPS id eb23sm53213963ibb.2.2011.11.21.17.43.43 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 21 Nov 2011 17:43:51 -0800 (PST) From: Shawn Guo To: linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org Subject: [RFC PATCH 2/3] arm/imx: add a generic clock implementation for imx Date: Tue, 22 Nov 2011 09:48:55 +0800 Message-Id: <1321926536-671-3-git-send-email-shawn.guo@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1321926536-671-1-git-send-email-shawn.guo@linaro.org> References: <1321926536-671-1-git-send-email-shawn.guo@linaro.org> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.210.177 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Arnd Bergmann , Sascha Hauer , Rob Herring , Grant Likely , Shawn Guo , Richard Zhao X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org It adds a generic clock implementation which applies on several modern imx generations. Signed-off-by: Shawn Guo --- 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 +#include +#include +#include +#include +#include +#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 +#include + +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 */