From patchwork Sat Sep 3 00:40:54 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: hotran X-Patchwork-Id: 9311827 X-Patchwork-Delegate: sboyd@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id ED612607D2 for ; Sat, 3 Sep 2016 00:42:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DC50729862 for ; Sat, 3 Sep 2016 00:42:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CF5A529880; Sat, 3 Sep 2016 00:42:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8434A29862 for ; Sat, 3 Sep 2016 00:42:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753082AbcICAlp (ORCPT ); Fri, 2 Sep 2016 20:41:45 -0400 Received: from mail-pf0-f172.google.com ([209.85.192.172]:36171 "EHLO mail-pf0-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752771AbcICAlP (ORCPT ); Fri, 2 Sep 2016 20:41:15 -0400 Received: by mail-pf0-f172.google.com with SMTP id h186so46918719pfg.3 for ; Fri, 02 Sep 2016 17:41:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=apm.com; s=apm; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=6+E0ztnHKgcRKKQZtE57mboEJ6qxcWyBP1SlRDx4LII=; b=eoomPQ+myeN9MVlHc+tRafjtdG4wtpR3bLQCj0azV5JnLuLNQs2VIhZhxKyA5s7hMq VDiulzS62IdILAlYSVbUU9mJq4LF7Ps5sQorJDerHLP6/bdV54XW+g0WSXotWeZJ5GYK kqaNCJz+uUpTKGM5jBVitf+8vkchwau0lUKR0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=6+E0ztnHKgcRKKQZtE57mboEJ6qxcWyBP1SlRDx4LII=; b=GqD0Q6qc0vn8M6vlb65cGjyXVC/8BuFkM2QwkP2QDeQuDdzMeTlrOvEn7hTj70O9/T VH6DkXWfegmKP+mkXLnrb+0mG4M9cosylaosxamKQOElp5ij0UC9tWhs9fZ5X6z4Ciar 5OhmBwRrxAawb9ZlYpwBbrcqZHLZT3Rzn7psNMcQ/BY9tLB/M65bZkxQiQ/ZbJMgvqDp qJBWllfqBviPP4/I2WVAVDc6by71h5L2NRBVMbkjdHImVug1DLY4lJOmE0zYDkQ8uruW oblFyBhtP6IHhJFRlfnVHSOmCq/l58lLjo3UC6e4ALuT8N6hoSjLY/ftFz10YpigqwHR X9Lw== X-Gm-Message-State: AE9vXwNkSYPENK0vU+3WUUGfSl4PuGWTraPbZ/m5aFu4JokLFCAekqUqbqKAYbUUnp09d8By X-Received: by 10.98.103.214 with SMTP id t83mr41162404pfj.158.1472863274297; Fri, 02 Sep 2016 17:41:14 -0700 (PDT) Received: from hotran_localhost.amcc.com ([206.80.4.98]) by smtp.gmail.com with ESMTPSA id f84sm17274407pfd.87.2016.09.02.17.41.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 02 Sep 2016 17:41:13 -0700 (PDT) From: Hoan Tran To: Michael Turquette , Rob Herring , Stephen Boyd , Mark Rutland Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, lho@apm.com, Duc Dang , Hoan Tran Subject: [PATCH v2 2/3] clk: xgene: Add PMD clock Date: Fri, 2 Sep 2016 17:40:54 -0700 Message-Id: <1472863255-32337-3-git-send-email-hotran@apm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1472863255-32337-1-git-send-email-hotran@apm.com> References: <1472863255-32337-1-git-send-email-hotran@apm.com> Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add X-Gene PMD clock support. PMD clock is implemented for a single register field. Output rate = parent_rate * (denominator - scale) / denominator with - denominator = bitmask of register field + 1 - scale = values of register field For example, for bitmask is 0x7, denominator will be 8 and scale will be computed and programmed accordingly. Signed-off-by: Hoan Tran --- drivers/clk/clk-xgene.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 3433132..5daddf5 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -217,6 +217,226 @@ static void xgene_pcppllclk_init(struct device_node *np) xgene_pllclk_init(np, PLL_TYPE_PCP); } +/** + * struct xgene_clk_pmd - PMD clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing the fractional scale multiplier (scaler) + * @shift: shift to the unit bit field + * @denom: 1/denominator unit + * @lock: register lock + * Flags: + * XGENE_CLK_PMD_SCALE_INVERTED - By default the scaler is the value read + * from the register plus one. For example, + * 0 for (0 + 1) / denom, + * 1 for (1 + 1) / denom and etc. + * If this flag is set, it is + * 0 for (denom - 0) / denom, + * 1 for (denom - 1) / denom and etc. + * + */ +struct xgene_clk_pmd { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u32 mask; + u64 denom; + u32 flags; + spinlock_t *lock; +}; + +#define to_xgene_clk_pmd(_hw) container_of(_hw, struct xgene_clk_pmd, hw) + +#define XGENE_CLK_PMD_SCALE_INVERTED BIT(0) +#define XGENE_CLK_PMD_SHIFT 8 +#define XGENE_CLK_PMD_WIDTH 3 + +static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + unsigned long flags = 0; + u64 ret, scale; + u32 val; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + else + __acquire(fd->lock); + + val = clk_readl(fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + else + __release(fd->lock); + + ret = (u64)parent_rate; + + scale = (val & fd->mask) >> fd->shift; + if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) + scale = fd->denom - scale; + else + scale++; + + /* freq = parent_rate * scaler / denom */ + do_div(ret, fd->denom); + ret *= scale; + if (ret == 0) + ret = (u64)parent_rate; + + return ret; +} + +static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + u64 ret, scale; + + if (!rate || rate >= *parent_rate) + return *parent_rate; + + /* freq = parent_rate * scaler / denom */ + ret = rate * fd->denom; + scale = DIV_ROUND_UP_ULL(ret, *parent_rate); + + ret = (u64)*parent_rate * scale; + do_div(ret, fd->denom); + + return ret; +} + +static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + unsigned long flags = 0; + u64 scale, ret; + u32 val; + + /* + * Compute the scaler: + * + * freq = parent_rate * scaler / denom, or + * scaler = freq * denom / parent_rate + */ + ret = rate * fd->denom; + scale = DIV_ROUND_UP_ULL(ret, (u64)parent_rate); + + /* Check if inverted */ + if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) + scale = fd->denom - scale; + else + scale--; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + else + __acquire(fd->lock); + + val = clk_readl(fd->reg); + val &= ~fd->mask; + val |= (scale << fd->shift); + clk_writel(val, fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + else + __release(fd->lock); + + return 0; +} + +static const struct clk_ops xgene_clk_pmd_ops = { + .recalc_rate = xgene_clk_pmd_recalc_rate, + .round_rate = xgene_clk_pmd_round_rate, + .set_rate = xgene_clk_pmd_set_rate, +}; + +static struct clk * +xgene_register_clk_pmd(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u8 shift, + u8 width, u64 denom, u32 clk_flags, spinlock_t *lock) +{ + struct xgene_clk_pmd *fd; + struct clk_init_data init; + struct clk *clk; + + fd = kzalloc(sizeof(*fd), GFP_KERNEL); + if (!fd) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &xgene_clk_pmd_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + fd->reg = reg; + fd->shift = shift; + fd->mask = (BIT(width) - 1) << shift; + fd->denom = denom; + fd->flags = clk_flags; + fd->lock = lock; + fd->hw.init = &init; + + clk = clk_register(dev, &fd->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(fd); + return NULL; + } + + return clk; +} + +static void xgene_pmdclk_init(struct device_node *np) +{ + const char *clk_name = np->full_name; + void __iomem *csr_reg; + struct resource res; + struct clk *clk; + u64 denom; + u32 flags = 0; + int rc; + + /* Check if the entry is disabled */ + if (!of_device_is_available(np)) + return; + + /* Parse the DTS register for resource */ + rc = of_address_to_resource(np, 0, &res); + if (rc != 0) { + pr_err("no DTS register for %s\n", np->full_name); + return; + } + csr_reg = of_iomap(np, 0); + if (!csr_reg) { + pr_err("Unable to map resource for %s\n", np->full_name); + return; + } + of_property_read_string(np, "clock-output-names", &clk_name); + + denom = BIT(XGENE_CLK_PMD_WIDTH); + flags |= XGENE_CLK_PMD_SCALE_INVERTED; + + clk = xgene_register_clk_pmd(NULL, clk_name, + of_clk_get_parent_name(np, 0), 0, + csr_reg, XGENE_CLK_PMD_SHIFT, + XGENE_CLK_PMD_WIDTH, denom, + flags, &clk_lock); + if (!IS_ERR(clk)) { + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + pr_debug("Add %s clock\n", clk_name); + } else { + if (csr_reg) + iounmap(csr_reg); + } +} + /* IP Clock */ struct xgene_dev_parameters { void __iomem *csr_reg; /* CSR for IP clock */ @@ -543,6 +763,7 @@ err: CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); +CLK_OF_DECLARE(xgene_pmd_clock, "apm,xgene-pmd-clock", xgene_pmdclk_init); CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock", xgene_socpllclk_init); CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock",