From patchwork Fri Oct 31 15:01:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulrich Hecht X-Patchwork-Id: 5205141 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A05959F349 for ; Fri, 31 Oct 2014 15:01:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 8B0AE20142 for ; Fri, 31 Oct 2014 15:01:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CAD3920172 for ; Fri, 31 Oct 2014 15:01:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932508AbaJaPBq (ORCPT ); Fri, 31 Oct 2014 11:01:46 -0400 Received: from mail-wi0-f178.google.com ([209.85.212.178]:46927 "EHLO mail-wi0-f178.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932570AbaJaPBp (ORCPT ); Fri, 31 Oct 2014 11:01:45 -0400 Received: by mail-wi0-f178.google.com with SMTP id q5so1540630wiv.11 for ; Fri, 31 Oct 2014 08:01:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=Y8cU/OFSzYQICWB9thLv6PHT0VIuHr8g9+FRhsbl99o=; b=Q4SyphO2bgywuHiATS6UKGosuPge7G4f1ftxutqBUyGs4F6fQzwy01PY5OwqodBAAR wPDaKb+e8PxiBvBcBdQfe8Mk0CIhj2JtFhwzsKPJwd6nYH1cdDYAXmV78QkWTwOPV0Is jz2wXq5iacHkox7/350tfTivCNIa4QZbBD/7gB8ApnTSQ9M6mcHFFyLW61qzwJOpMeTi GOY/JgDclg2LSwFS39DywKaZFGJmaa1gNcJNFF4OT054lXUamSyHiYsZt82lyB8ec+r1 rZT0MSQiBdpzjTykmx5OL2/vh4u8ugEeShlGJE3weXFRU+y9peSf1L+zA+URzqhy2tCj LzBg== X-Received: by 10.181.13.208 with SMTP id fa16mr4345443wid.61.1414767704080; Fri, 31 Oct 2014 08:01:44 -0700 (PDT) Received: from groucho.site ([37.203.209.10]) by mx.google.com with ESMTPSA id q10sm12355714wjq.35.2014.10.31.08.01.41 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 31 Oct 2014 08:01:42 -0700 (PDT) From: Ulrich Hecht To: horms@verge.net.au, mturquette@linaro.org, laurent.pinchart+renesas@ideasonboard.com Cc: linux-sh@vger.kernel.org, magnus.damm@gmail.com, geert@linux-m68k.org, devicetree@vger.kernel.org, Ulrich Hecht Subject: [PATCH 1/2] clk: shmobile: div6: support selectable-input clocks Date: Fri, 31 Oct 2014 16:01:35 +0100 Message-Id: <1414767696-23211-2-git-send-email-ulrich.hecht+renesas@gmail.com> X-Mailer: git-send-email 1.8.4.5 In-Reply-To: <1414767696-23211-1-git-send-email-ulrich.hecht+renesas@gmail.com> References: <1414767696-23211-1-git-send-email-ulrich.hecht+renesas@gmail.com> Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-7.2 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SBL, RP_MATCHES_RCVD, T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Support for setting the parent at initialization time based on the current hardware configuration in DIV6 clocks with selectable parents as found in the r8a73a4, r8a7740, sh73a0, and other SoCs. Signed-off-by: Ulrich Hecht --- drivers/clk/shmobile/clk-div6.c | 117 +++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c index f065f69..ea44988 100644 --- a/drivers/clk/shmobile/clk-div6.c +++ b/drivers/clk/shmobile/clk-div6.c @@ -32,6 +32,9 @@ struct div6_clock { struct clk_hw hw; void __iomem *reg; unsigned int div; + int src_shift; + int src_width; + u8 *parents; }; #define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw) @@ -39,8 +42,11 @@ struct div6_clock { static int cpg_div6_clock_enable(struct clk_hw *hw) { struct div6_clock *clock = to_div6_clock(hw); + u32 val; - clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg); + val = (clk_readl(clock->reg) & ~(CPG_DIV6_DIV_MASK | CPG_DIV6_CKSTP)) + | CPG_DIV6_DIV(clock->div - 1); + clk_writel(val, clock->reg); return 0; } @@ -52,7 +58,7 @@ static void cpg_div6_clock_disable(struct clk_hw *hw) /* DIV6 clocks require the divisor field to be non-zero when stopping * the clock. */ - clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK), + clk_writel(clk_readl(clock->reg) | CPG_DIV6_CKSTP | CPG_DIV6_DIV_MASK, clock->reg); } @@ -94,12 +100,53 @@ static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate, { struct div6_clock *clock = to_div6_clock(hw); unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate); + u32 val; clock->div = div; + val = clk_readl(clock->reg) & ~CPG_DIV6_DIV_MASK; /* Only program the new divisor if the clock isn't stopped. */ - if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP)) - clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg); + if (!(val & CPG_DIV6_CKSTP)) + clk_writel(val | CPG_DIV6_DIV(clock->div - 1), clock->reg); + + return 0; +} + +static unsigned char cpg_div6_clock_get_parent(struct clk_hw *hw) +{ + struct div6_clock *clock = to_div6_clock(hw); + u8 hw_index; + int i; + + if (clock->src_width == 0) + return 0; + + hw_index = (clk_readl(clock->reg) >> clock->src_shift) & + (BIT(clock->src_width) - 1); + for (i = 0; i < hw->init->num_parents; i++) { + if (clock->parents[i] == hw_index) + return i; + } + + pr_err("%s: %s DIV6 clock set to invalid parent %d\n", + __func__, hw->init->name, hw_index); + return 0; +} + +static int cpg_div6_clock_set_parent(struct clk_hw *hw, u8 index) +{ + struct div6_clock *clock = to_div6_clock(hw); + u8 hw_index; + u32 mask; + + if (index >= hw->init->num_parents) + return -EINVAL; + + mask = ~((BIT(clock->src_width) - 1) << clock->src_shift); + hw_index = clock->parents[index]; + + clk_writel((clk_readl(clock->reg) & mask) | + (hw_index << clock->src_shift), clock->reg); return 0; } @@ -108,6 +155,8 @@ static const struct clk_ops cpg_div6_clock_ops = { .enable = cpg_div6_clock_enable, .disable = cpg_div6_clock_disable, .is_enabled = cpg_div6_clock_is_enabled, + .get_parent = cpg_div6_clock_get_parent, + .set_parent = cpg_div6_clock_set_parent, .recalc_rate = cpg_div6_clock_recalc_rate, .round_rate = cpg_div6_clock_round_rate, .set_rate = cpg_div6_clock_set_rate, @@ -115,12 +164,17 @@ static const struct clk_ops cpg_div6_clock_ops = { static void __init cpg_div6_clock_init(struct device_node *np) { + const char **parent_names; struct clk_init_data init; struct div6_clock *clock; - const char *parent_name; const char *name; struct clk *clk; + int valid_parents; + int num_parents; + u32 src_shift; + u32 src_width; int ret; + int i; clock = kzalloc(sizeof(*clock), GFP_KERNEL); if (!clock) { @@ -129,6 +183,24 @@ static void __init cpg_div6_clock_init(struct device_node *np) return; } + num_parents = of_clk_get_parent_count(np); + if (num_parents < 1) { + pr_err("%s: no parent found for %s DIV6 clock\n", + __func__, np->name); + return; + } + + clock->parents = kmalloc_array(num_parents, sizeof(*clock->parents), + GFP_KERNEL); + parent_names = kmalloc_array(num_parents, sizeof(*parent_names), + GFP_KERNEL); + + if (!parent_names) { + pr_err("%s: failed to allocate %s parent name list\n", + __func__, np->name); + return; + } + /* Remap the clock register and read the divisor. Disabling the * clock overwrites the divisor, so we need to cache its value for the * enable operation. @@ -150,19 +222,38 @@ static void __init cpg_div6_clock_init(struct device_node *np) goto error; } - parent_name = of_clk_get_parent_name(np, 0); - if (parent_name == NULL) { - pr_err("%s: failed to get %s DIV6 clock parent name\n", - __func__, np->name); - goto error; + + for (i = 0, valid_parents = 0; i < num_parents; i++) { + const char *name = of_clk_get_parent_name(np, i); + + if (name) { + parent_names[valid_parents] = name; + clock->parents[valid_parents] = i; + valid_parents++; + } + } + + if (!of_property_read_u32(np, "renesas,src-shift", &src_shift)) { + if (!of_property_read_u32(np, "renesas,src-width", + &src_width)) { + clock->src_shift = src_shift; + clock->src_width = src_width; + } else { + pr_err("%s: renesas,src-shift without renesas,src-width in %s\n", + __func__, np->name); + goto error; + } + } else { + clock->src_shift = clock->src_width = 0; } + /* Register the clock. */ init.name = name; init.ops = &cpg_div6_clock_ops; init.flags = CLK_IS_BASIC; - init.parent_names = &parent_name; - init.num_parents = 1; + init.parent_names = parent_names; + init.num_parents = valid_parents; clock->hw.init = &init; @@ -175,11 +266,13 @@ static void __init cpg_div6_clock_init(struct device_node *np) of_clk_add_provider(np, of_clk_src_simple_get, clk); + kfree(parent_names); return; error: if (clock->reg) iounmap(clock->reg); + kfree(parent_names); kfree(clock); } CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init);