From patchwork Mon Nov 3 16: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: 5217941 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id ECC3DC11AC for ; Mon, 3 Nov 2014 16:02:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C7E712014A for ; Mon, 3 Nov 2014 16:02:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A655D20114 for ; Mon, 3 Nov 2014 16:02:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752324AbaKCQCG (ORCPT ); Mon, 3 Nov 2014 11:02:06 -0500 Received: from mail-la0-f54.google.com ([209.85.215.54]:51215 "EHLO mail-la0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752082AbaKCQCF (ORCPT ); Mon, 3 Nov 2014 11:02:05 -0500 Received: by mail-la0-f54.google.com with SMTP id s18so4366352lam.27 for ; Mon, 03 Nov 2014 08:02:02 -0800 (PST) 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=yFHbo+eTywweHCsrVKNc8mQoG+0IiFhWV/O62rJK/vU=; b=LMqKxxpOwZUMCMqeTk2c3OlAyjrEXRzBnw2lYtHd0tlnq8cmQlRdL224RnrQSl++BY mIVa2VcXWdBfojYPbE7cUFLrmzQXOWIdXYReT0Vo8nfNqbJGiAonDw30UhPtCRGLp5Tu Kyq0+ipecY94v9LAy5tmCtazZ+oq0EzbaiYq/Vbg4diXqhwzNUDnfSGbYMgRnYFITk21 1t/hTArRm1blEYbCHndF/Y2C30ufBZNblKSJ+eP09E3AgwLNq4lHNFKVZQU6apUycV5q chYJ/XjrFzKb/yi1Skh9pFFR6kotMyVgk5iO1EexPB5VIKSuvTG8xzBEuGsreYIakUgP dAUw== X-Received: by 10.112.130.41 with SMTP id ob9mr51074698lbb.74.1415030522864; Mon, 03 Nov 2014 08:02:02 -0800 (PST) Received: from groucho.site ([37.203.209.10]) by mx.google.com with ESMTPSA id n10sm8106692laf.38.2014.11.03.08.02.00 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 03 Nov 2014 08:02:01 -0800 (PST) From: Ulrich Hecht To: horms@verge.net.au, mturquette@linaro.org, geert@linux-m68k.org, laurent.pinchart+renesas@ideasonboard.com Cc: linux-sh@vger.kernel.org, magnus.damm@gmail.com, devicetree@vger.kernel.org, mark.rutland@arm.com, Ulrich Hecht Subject: [PATCH v6 1/2] clk: shmobile: div6: support selectable-input clocks Date: Mon, 3 Nov 2014 17:01:35 +0100 Message-Id: <1415030496-26054-2-git-send-email-ulrich.hecht+renesas@gmail.com> X-Mailer: git-send-email 1.8.4.5 In-Reply-To: <1415030496-26054-1-git-send-email-ulrich.hecht+renesas@gmail.com> References: <1415030496-26054-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 | 110 +++++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c index f065f69..91ca1fd 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; + u32 src_shift; + u32 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 u8 cpg_div6_clock_get_parent(struct clk_hw *hw) +{ + struct div6_clock *clock = to_div6_clock(hw); + unsigned int i; + u8 hw_index; + + 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 < __clk_get_num_parents(hw->clk); i++) { + if (clock->parents[i] == hw_index) + return i; + } + + pr_err("%s: %s DIV6 clock set to invalid parent %u\n", + __func__, __clk_get_name(hw->clk), 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 >= __clk_get_num_parents(hw->clk)) + 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,20 +164,33 @@ static const struct clk_ops cpg_div6_clock_ops = { static void __init cpg_div6_clock_init(struct device_node *np) { + unsigned int num_parents, valid_parents; + const char **parent_names; struct clk_init_data init; struct div6_clock *clock; - const char *parent_name; const char *name; struct clk *clk; + unsigned int i; int ret; clock = kzalloc(sizeof(*clock), GFP_KERNEL); - if (!clock) { - pr_err("%s: failed to allocate %s DIV6 clock\n", + if (!clock) + 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) + 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 +212,37 @@ 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 (num_parents > 4) { + /* VCLK with EXSRC bits 12-14 */ + clock->src_shift = 12; + clock->src_width = 3; + } else if (num_parents > 1) { + /* clock with EXSRC bits 6-7 */ + clock->src_shift = 6; + clock->src_width = 2; + } else { + /* fixed parent clock */ + 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 +255,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);